Xcode 7.3已停用“++”和“--”运算符。

148

我正在查看Xcode 7.3的笔记,并注意到了这个问题。

++和--操作符已被弃用。

可以有人解释一下为什么它被弃用了吗?在新版本的 Xcode 中,现在会使用 x += 1 来替代 ++ 吗?

例如:

for var index = 0; index < 3; index += 1 {
    print("index is \(index)")
}

警告截图


6
我认为这个问题超出了stackoverflow的范围,主要是因为所有被接受的Swift演进提案都可以在Github上找到,你可以阅读更多关于该提案的原因:https://github.com/apple/swift-evolution/blob/master/proposals/0004-remove-pre-post-inc-decrement.md。 - Victor Sigler
7
我正在认真考虑回到Objective-C。跟进Swift的所有变化不值得这么做。 - Greg Brown
3
@OlegGordiichuk,这个事情是关于移除C风格的for循环,详情请见https://github.com/Vkt0r/swift-evolution/blob/master/proposals/0007-remove-c-style-for-loops.md,因此你不需要再使用`++`和`--`运算符了。 - Victor Sigler
10
对于我来说,有太多的重大更改。 我赞成改进,但我真的不想每次Xcode发布新版本时都花费时间重写大部分代码库。 - Greg Brown
4
@Fogmeister 我不确定我应该如何表达更清楚。我更喜欢使用Swift,但我觉得它还不够稳定。我过去曾广泛使用其他语言,但在如此短的时间内从未遇到过如此多的重大变化。我感觉苹果想让我们都采用Swift,但他们正在使这变得比本应该的更加困难。 - Greg Brown
显示剩余12条评论
12个回答

221

这里有Chris Lattner的完整解释,Swift的创始人。我将总结一下要点:

  1. 在学习Swift时,你还要学习它,增加了负担。
  2. 没有比x += 1更短的表达方式。
  3. Swift不是C语言。为了取悦C程序员而保留它们是不必要的。
  4. 其主要用途是在C风格的for循环中:for i = 0; i < n; i++ { ... },而Swift有更好的替代方案,如for i in 0..(C式for循环也即将淘汰)。
  5. 阅读和维护起来可能会比较棘手,例如,x- ++xfoo(++x, x++)的值是什么?
  6. Chris Lattner不喜欢它。

对于那些感兴趣的人(并为了避免链接失效),Lattner用自己的话解释了原因:

  1. 这些运算符增加了学习Swift作为第一编程语言的负担,或者在您不从其他语言中已经知道这些运算符的情况下的任何情况。

  2. 它们的表达能力优势很小,x++并没有比x += 1短多少。

  3. Swift已经偏离了C语言的模式,=、+=和其他类赋值操作返回Void(由于多种原因),这些运算符与该模式不一致。

  4. Swift具有强大的功能,可以消除在其他语言中使用++i C风格for循环的许多常见原因,因此在编写良好的Swift代码中相对较少使用。这些特性包括for-in循环、范围、枚举、映射等等。

  5. 这些运算符实际使用其结果值的代码常常会让代码的读者/维护者感到困惑和微妙。它们鼓励编写“过于棘手”的代码,可能很巧妙,但难以理解。

  6. 虽然 Swift 有明确定义的计算顺序,但任何依赖此顺序的代码(如 foo(++a, a++))即使定义良好也不应该出现。

  7. 这些运算符适用于相对较少的类型:整数和浮点标量、类似迭代器的概念。它们不适用于复数、矩阵等。

最后,这些运算符不符合“如果我们没有它们,我们会在 Swift 3 中添加它们吗?”的标准。


56
我认为,真正的答案是数字6。没关系,我们(曾经的C、Java程序员等)足够灵活:-)。一般来说,对于现实世界,变异、交叉和选择已经足够了。我、你和Cris都是这三个操作符的结果... - user3441734
6
第五点:在C语言中,这些都是与具体实现相关的,而且任何有头脑的人都不会这样做。只需定义行为,我们就会适应它。这比毫无意义地回头改变完全正常的旧代码要好。 - Echelon
3
我喜欢第三点。你不能永远被遗留合同所束缚。我热爱C语言,但是你正在创造一种新的编程语言;重新开始是有意义的,让你能够从一张空白的画布开始。 - Nicolas Miari
10
这是因为苹果喜欢强迫你像他们一样思考。我认为这在任何需要增加或减少变量的地方都可以使用,非常合适。它不是必须学习的内容,即使不掌握也不会有太大问题。而第5条只是编写不好的代码,我从未见过这样的代码。所以选择第6条。废弃它已经让我感到困惑并进行了一次谷歌搜索,所以谢谢Chris浪费我的时间。 - csga5000
4
考虑到如果您真的想要,您可以自己定义运算符,因此这是一个相当不充分的论据。这与苹果希望人们像他们一样思考无关,只是因为它不适合该语言。如果 C 风格语言中不存在 ++ ,那么没有人会理智地看待 Swift 3.0 的设计,并认为将 ++ 运算符作为其中的一个很好的补充。 - overactor
显示剩余16条评论

41
我知道这条评论并没有回答问题,但是可能有人正在寻找如何让这些运算符继续工作的解决方案,这样的解决方案可以在底部找到。
我个人更喜欢使用++--运算符。我不同意它们很棘手或难以管理的观点。一旦开发人员理解了这些运算符所做的事情(而我们谈论的是相当简单的东西),代码应该非常清晰。
在解释为什么这些运算符被弃用时提到它们的主要用途是C风格的循环。我不知道别人怎么想,但我个人根本不使用C风格的循环,还有许多其他场合或情况下++--运算符非常有用。
我还想提一下,varName++返回一个值,因此可以在return中使用,而varName += 1则不行。
对于任何想让这些运算符继续工作的人,以下是解决方案:
prefix operator ++ {}
postfix operator ++ {}

prefix operator -- {}
postfix operator -- {}


// Increment
prefix func ++(inout x: Int) -> Int {
    x += 1
    return x
}

postfix func ++(inout x: Int) -> Int {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: UInt) -> UInt {
    x += 1
    return x
}

postfix func ++(inout x: UInt) -> UInt {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: Int8) -> Int8 {
    x += 1
    return x
}

postfix func ++(inout x: Int8) -> Int8 {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: UInt8) -> UInt8 {
    x += 1
    return x
}

postfix func ++(inout x: UInt8) -> UInt8 {
    x += 1
    return (x - 1)
}
prefix func ++(inout x: Int16) -> Int16 {
    x += 1
    return x
}

postfix func ++(inout x: Int16) -> Int16 {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: UInt16) -> UInt16 {
    x += 1
    return x
}

postfix func ++(inout x: UInt16) -> UInt16 {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: Int32) -> Int32 {
    x += 1
    return x
}

postfix func ++(inout x: Int32) -> Int32 {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: UInt32) -> UInt32 {
    x += 1
    return x
}

postfix func ++(inout x: UInt32) -> UInt32 {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: Int64) -> Int64 {
    x += 1
    return x
}

postfix func ++(inout x: Int64) -> Int64 {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: UInt64) -> UInt64 {
    x += 1
    return x
}

postfix func ++(inout x: UInt64) -> UInt64 {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: Double) -> Double {
    x += 1
    return x
}

postfix func ++(inout x: Double) -> Double {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: Float) -> Float {
    x += 1
    return x
}

postfix func ++(inout x: Float) -> Float {
    x += 1
    return (x - 1)
}

prefix func ++(inout x: Float80) -> Float80 {
    x += 1
    return x
}

postfix func ++(inout x: Float80) -> Float80 {
    x += 1
    return (x - 1)
}

prefix func ++<T : _Incrementable>(inout i: T) -> T {
    i = i.successor()
    return i
}

postfix func ++<T : _Incrementable>(inout i: T) -> T {
    let y = i
    i = i.successor()
    return y
}

// Decrement
prefix func --(inout x: Int) -> Int {
    x -= 1
    return x
}

postfix func --(inout x: Int) -> Int {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: UInt) -> UInt {
    x -= 1
    return x
}

postfix func --(inout x: UInt) -> UInt {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: Int8) -> Int8 {
    x -= 1
    return x
}

postfix func --(inout x: Int8) -> Int8 {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: UInt8) -> UInt8 {
    x -= 1
    return x
}

postfix func --(inout x: UInt8) -> UInt8 {
    x -= 1
    return (x + 1)
}
prefix func --(inout x: Int16) -> Int16 {
    x -= 1
    return x
}

postfix func --(inout x: Int16) -> Int16 {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: UInt16) -> UInt16 {
    x -= 1
    return x
}

postfix func --(inout x: UInt16) -> UInt16 {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: Int32) -> Int32 {
    x -= 1
    return x
}

postfix func --(inout x: Int32) -> Int32 {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: UInt32) -> UInt32 {
    x -= 1
    return x
}

postfix func --(inout x: UInt32) -> UInt32 {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: Int64) -> Int64 {
    x -= 1
    return x
}

postfix func --(inout x: Int64) -> Int64 {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: UInt64) -> UInt64 {
    x -= 1
    return x
}

postfix func --(inout x: UInt64) -> UInt64 {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: Double) -> Double {
    x -= 1
    return x
}

postfix func --(inout x: Double) -> Double {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: Float) -> Float {
    x -= 1
    return x
}

postfix func --(inout x: Float) -> Float {
    x -= 1
    return (x + 1)
}

prefix func --(inout x: Float80) -> Float80 {
    x -= 1
    return x
}

postfix func --(inout x: Float80) -> Float80 {
    x -= 1
    return (x + 1)
}

prefix func --<T : BidirectionalIndexType>(inout i: T) -> T {
    i = i.predecessor()
    return i
}

postfix func --<T : BidirectionalIndexType>(inout i: T) -> T {
    let y = i
    i = i.predecessor()
    return y
}

2
我不喜欢你的后缀运算符 return (x - 1) - 在我看来,保持它们返回(原始值的副本)的语义比返回 x + 1 - 1 更加清晰。 - Alnitak
1
我明白了,我不想为了创建另一个变量(或者在这种情况下更确切地说是常量)而这样做。如果我们只考虑 Int 类型,那么 (x + 1) 的结果将会溢出,这将中断执行,因此 result - 1 将不会被执行。其他数据类型如 Double 则表现不同,所以我需要进一步调查。 - 0101
4
您也可以使用defer来实现这一点。defer { x += 1 }; return x - Tim Vermeulen
你能分享一下创建这个脚本的代码吗?还是你自己写的? :) - Orkhan Alikhanov
5
为什么不使用泛型并用几行代码来编写这个? - μολὼν.λαβέ
显示剩余14条评论

22

苹果已经移除了 ++,并使用另一种老传统方式简化了它。

你需要使用 += 代替 ++

例子:

var x = 1

//Increment
x += 1 //Means x = x + 1 

对于递减操作符--,你需要写-=

例如:

var x = 1

//Decrement
x -= 1 //Means x = x - 1

对于 for 循环:

自增示例:

不要使用

for var index = 0; index < 3; index ++ {
    print("index is \(index)")
}

您可以写:

//Example 1
for index in 0..<3 {
    print("index is \(index)")
}

//Example 2
for index in 0..<someArray.count {
    print("index is \(index)")
}

//Example 3
for index in 0...(someArray.count - 1) {
    print("index is \(index)")
}

减量示例:

for var index = 3; index >= 0; --index {
   print(index)
}

你可以写:

for index in 3.stride(to: 1, by: -1) {
   print(index)
}
//prints 3, 2

for index in 3.stride(through: 1, by: -1) {
   print(index)
}
//prints 3, 2, 1

for index in (0 ..< 3).reverse() {
   print(index)
}

for index in (0 ... 3).reverse() {
   print(index)
}
希望这有所帮助!

2
他们没有替换任何东西;+= 一直存在。 - Nicolas Miari
@NicolasMiari 嗯,只是用更好的格式进行编辑。 - Sohil R. Memon
@NicolasMiari 你现在可以检查一下吗? - Sohil R. Memon
3
++i--i 代表对变量 i 的自增和自减操作,在表达式中使用时可以先进行自增或自减操作再参与后续的计算。 - Zigii Wong

9
对于Swift 4,你可以将++--操作符作为Int和其他类型的扩展进行恢复。以下是一个示例:
extension Int {
   @discardableResult
   static prefix func ++(x: inout Int) -> Int {
        x += 1
        return x
    }

    static postfix func ++(x: inout  Int) -> Int {
        defer {x += 1}
        return x
    }

    @discardableResult
    static prefix func --(x: inout Int) -> Int {
        x -= 1
        return x
    }

    static postfix func --(x: inout Int) -> Int {
        defer {x -= 1}
        return x
    }
}

对于其他类型,比如 UIInt, Int8, Float, Double 等,它们的工作方式也是相同的。

你可以将这些扩展粘贴到根目录下的单个文件中,它们将可用于所有其他文件中。如果在 playground 中检查,它完美地工作。


7
Chris Lattner对++和--进行了反对。他写道,“实际使用这些运算符结果的代码经常会让编写者/代码维护者感到困惑和微妙。它们鼓励编写“过于巧妙”的代码,这些代码可能很可爱,但难以理解......Swift虽然有明确定义的求值顺序,但任何依赖它的代码(如foo(++a, a++))即使是被明确定义的也不会受欢迎......这些运算符未通过“如果我们没有这些东西,我们是否会将它们添加到Swift 3?”测试指标。”
苹果公司希望保持Swift语言简洁、明确、无混淆和直截了当。所以他们弃用了++和--关键字。

9
"干净?看看这个回调地狱,还说是干净的?我不同意……而且我要补充一点:别碰++和--" - mcatach
23
类似于 ...for i in 0.stride(to: 10, by: 2)... 或者 ...for i in (1...10).reverse()... 这样的写法简洁明了吗? - mad_manny
6
我同意。关于“干净”的争论在 Swift 的其他方面上基本上是自相矛盾的。作为一个来自客观上不够干净的 Objective-C 的编程语言,很难接受“干净”作为苹果编程语言目标的说法。 - Adrian Bartholomew
3
尝试解析 JSON 和 Swift,告诉我它们的清晰程度如何。 - nickthedude

5

这是目前为止发布的代码的一个通用版本。我和其他人一样都认为,不应该在Swift中使用这些方法,这是最佳实践。我同意,这可能会让未来阅读你的代码的人感到困惑。

prefix operator ++
prefix operator --
prefix func ++<T: Numeric> (_ val: inout T) -> T {
    val += 1
    return val
}

prefix func --<T: Numeric> (_ val: inout T) -> T {
    val -= 1
    return val
}

postfix operator ++
postfix operator --
postfix func ++<T: Numeric> (_ val: inout T) -> T {
    defer { val += 1 }
    return val
}

postfix func --<T: Numeric> (_ val: inout T) -> T {
    defer { val -= 1 }
    return val
}

这也可以作为Numeric类型的扩展来编写。

我在这些函数中添加了@discardableResult以消除有关未使用返回值的警告;否则正是我要找的。 - Devin Lane

5

警告截图

Xcode的“修复功能”可以清楚地回答这个问题。

警告解决方案

用老式的“value += 1”(简写运算符)替换“++自增运算符”,用“value -= 1”替换“--自减运算符”。


4

文档中得知:

Swift的递增/递减运算符非常早就加入了Swift的开发,是从C语言中继承过来的。这些运算符在加入时并没有经过多少考虑,并且自那时以来也没有得到多少关注。本文提供了一个新的视角来看待它们,并最终建议我们完全删除它们,因为它们令人困惑,而且没有起到作用。


2
在这里你可以阅读关于它的内容,但并不是因为它很昂贵,而是语言设计的原因。 - Dániel Nagy
所以,苹果公司将会在 Swift 中停止支持 C 风格的特性。 - Oleg Gordiichuk
@OlegGordiichuk 我认为Swift的设计并不是面向现有开发人员(或那些对成为开发人员充满热情的人),而是面向那些可能没有成为开发人员的倾向,以便让那些没有开发思维过程的人实现良好的设计。这当然不是贬低那些决定转向该语言的众多优秀开发人员!只是我的观点是,这些开发人员不是该语言的目标受众。 - mah
2
@OlegGordiichuk 嗯,我想说他们想强调Swift不像Objective-C一样是C的超集。 - Dániel Nagy
1
@mah,你说的很多话根本就没有意义。 “不面向现有开发人员”是什么意思?就像Java不面向PHP开发人员一样吗?“面向那些可能没有成为开发人员的倾向”?是的,因为所有那些非开发人员都在使用协议导向编程和泛型。 “实现良好设计的一种方式”,只需看看SO,你会发现没有任何编程语言可以“实现良好设计”。 - Fogmeister
显示剩余5条评论

0
var value : Int = 1

func theOldElegantWay() -> Int{
return value++
}

func theNewFashionWay() -> Int{
let temp = value
value += 1
return temp
}

这肯定是一个不利因素,对吧?


5
你的意思是优雅,就像“你必须记住C编程语言的所有细微差别,否则第一次调用返回1还是2不会立即明显”?我想我们可以多写几行代码,以换取不花费几分钟来寻找由愚蠢错误导致的错误。 - Nicolas Miari

0
在没有分号的语言中,它可能会产生歧义。这是前缀还是后缀运算符?
考虑以下例子:
var x = y
++x

人类读取++x,但解析器可能会将其读取为y++


额...关于 var x = y - 结尾处的 \n,我很确定编写 Swift 解析器的人不是第一天上班。 - Chris Walken

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接