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


139

我正在查看Xcode 7.3注释,并且注意到了这个问题。

++和-运算符已被弃用

有人可以解释为什么不推荐使用它吗?我说对了,现在在新版本的Xcode中,您将使用它代替++x += 1

例:

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

屏幕截图警告


6
我认为这个问题是否超出了stackoverflow的范围,主要是因为可以在Github中找到所有接受的关于快速发展的提议,您可以阅读有关该提议的原因的更多信息github.com/apple/swift-evolution/blob/master / proposals /…
Victor Sigler

7
我正在认真考虑只返回到Objective-C。紧跟Swift的所有更改是不值得的。
格雷格·布朗

3
@OlegGordiichuk这也是因为C风格的for循环也将被删除,请参见github.com/Vkt0r/swift-evolution/blob/master/proposals/…,因此您不需要使用更多的++and --运算符
Victor Sigler

10
我的口味有太多重大变化。我全力以赴,但我真的不想花时间在每次发布Xcode point版本时重写代码库的大部分内容。
格雷格·布朗

4
@Fogmeister我不确定该如何清除。我更喜欢使用Swift,但是我觉得它不够稳定。过去,我与其他语言进行过广泛的合作,并且从未在如此短的时间内遇到如此多的重大更改。我感觉苹果公司希望我们所有人都采用Swift,但是他们使它变得比想象中要困难得多。
格雷格·布朗

Answers:


210

Swift的创建者Chris Lattner 在这里提供了完整的解释。我将总结几点:

  1. 这是学习Swift时必须学习的另一个功能
  2. 不短于 x += 1
  3. Swift不是C。不应仅仅为了取悦C程序员而采用它们
  4. 它的主要用途是C样式的for循环:for i = 0; i < n; i++ { ... },Swift有更好的选择,例如for i in 0..<n { ... }(C样式的for循环也将淘汰
  5. 读取和维护可能很棘手,例如x - ++xor 的值是foo(++x, x++)多少?
  6. 克里斯·拉特纳(Chris Lattner)不喜欢。

对于那些感兴趣的人(并避免链接腐烂),Lattner用他自己的话讲的理由是:

  1. 这些运算符增加了学习Swift作为第一门编程语言的负担-或在其他情况下您还不了解其他语言的运算符。

  2. 它们的表达优势极小-x ++比x + = 1短很多。

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

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

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

  6. 尽管Swift具有明确定义的求值顺序,但是依赖于它的任何代码(如foo(++ a,a ++))也将是不可取的,即使它定义得很好。

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

最后,这些没有通过“如果我们还没有这些,我们可以将它们添加到Swift 3吗?”的度量标准。


54
我的话,真正的答案是数字6。好的,我们(以前的C,Java,...程序员)足够灵活:-)。通常,对于现实世界而言,变异,交叉和选择就足够了。我,您和克里斯也是如此,我们都是这三个运算符的结果……
user3441734 '16

5
第5点:在C语言中,它们始终依赖于实现,并且从没有任何意义上做到。只需定义行为,我们就会习惯它。总比没有理由无缘无故地更改完美的旧代码更好。
梯队

3
我喜欢第3点。您永远不会束缚传统合同。我喜欢C,但是您正在创建一种新的编程语言。从您需要的位置开始尽可能干净就可以了。
Nicolas Miari '16

8
这是因为苹果喜欢强迫您像他们一样思考。我认为它非常好,可以在需要递增或递减变量的任何地方使用。这不是您“必须学习”的东西,没有它您就不会做的很好。#5只是写得不好的代码,我从未见过这样的代码。所以是#6。描述它足以使我挠头并进行谷歌搜索,因此感谢您浪费我的时间克里斯。
csga5000 '16

4
@ csga5000考虑到您可以自己定义运算符,所以这是一个很弱的参数。这与苹果希望人们像他们一样思考无关。它根本不适合该语言。如果++在C风格的语言中不存在它们,那么没有人会想到Swift 3.0的设计,并认为++运算符将是对它的很好补充。
overactor

37

我意识到,尽管如此,此评论仍无法解决问题,可能有人会在寻找一种解决方案,以使这些操作员保持工作状态,这种解决方案可以在底部找到。😇

我个人更喜欢++--运营商。我不同意它们棘手或难以管理的观点。一旦开发人员了解了这些运算符的功能(并且我们正在谈论非常简单的内容),则代码应该非常清楚。

在解释为什么不推荐使用运算符时,提到了它们的主要用途是C风格的for循环。我对其他人一无所知,但我个人根本不使用C风格的循环,在还有其他地方或情况下,++--运算符很有用。

我还要提及的是,它varName++返回一个值,因此可以在returnwhile中使用它,而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
}

return (x - 1)对于后缀运算符,我不喜欢您-恕我直言,保持它们返回原始值(的副本)的语义会更干净,而不是您得到的结果x + 1 - 1
Alnitak

我也不喜欢,但我不知道其他任何方式(更好,更清洁)。我不完全理解你的第二点。
0101年

1
我知道,我不想这样做只是为了创建另一个变量(在这种情况下为常量)。如果Int仅在讨论,则的结果(x + 1)将溢出,这将中断执行,因此result - 1甚至不会运行。Double例如,其他数据类型的行为却有所不同,因此我需要对此进行调查。
0101年

3
您也可以使用defer它。defer { x += 1 }; return x
Tim Vermeulen

4
为什么不使用泛型并将其写成几行呢?
μολὼν.λαβέ

22

Apple已删除了,++并通过另一种旧的传统方式使其变得更加简单。

相反++,您需要编写+=

例:

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)
}

希望这可以帮助!


他们什么也没替换。+=一直在那里。
Nicolas Miari '16

@NicolasMiari是的,只是编辑了更好的格式
Sohil R. Memon 2016年

@NicolasMiari您能现在检查一下吗?
Sohil R. Memon

3
怎么样++i--i
Zigii Wong

7

克里斯·拉特纳(Chris Lattner)与++和-进行斗争。他写道:“实际上使用这些运算符的结果值的代码通常会使代码的读取器/维护者感到困惑和微妙。他们鼓励使用“过于棘手”的代码,这些代码可能很可爱,但很难理解……。尽管Swift的评估顺序已明确定义,但是依赖于该代码的任何代码(例如foo(++ a,a ++))都将是不受欢迎的定义明确...这些指标无法通过“如果我们还没有这些,我们可以将它们添加到Swift 3吗?”

苹果公司希望保持一种简洁,清晰,不混乱和直截了当的语言。因此,他们弃用了++和-关键字。


9
清洁?看看这个回调地狱,称它干净吗?我不同意...而且我要补充一点:让++和-独自一人
mcatach

22
...for i in 0.stride(to: 10, by: 2)...还是...for i in (1...10).reverse()...干净的东西?
mad_manny '16

6
我同意。“干净”的论点与其他Swift根本上是矛盾的。来自客观上不干净的Objective-C,很难接受“干净”作为Apple语言的目标。
阿德里安·巴塞洛缪

2
尝试解析json和swift,然后告诉我它有多干净。
nickthedude

6

屏幕截图警告

Fix-it featureXcode中的提供了明确的答案。

警告解决方案

更换++ increment operator用老式的value += 1(短手操作),并-- decrement operatorvalue -= 1


6

对于Swift 4,您可以将++--运算符还原为Int和其他类型的扩展。这是一个例子:

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

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

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

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

它的工作原理相同的方式对其他类型,如UIIntInt8FloatDouble,等。

您可以将这些扩展名粘贴到根目录中的单个文件中,并将在其中的所有其他文件中使用它们。

我注意到,几乎是在我发布该评论后,我在这里对我的回答进行了几次否决。我将其视为哲学上的分歧,而不是批评我的代码如何工作。如果您在操场上签出,它会完美地工作。

我之所以发布此答案,是因为我不同意使计算机编程语言彼此不必要地有所不同。

语言之间有很多相似之处,这使人们更容易学习和从一种语言切换到另一种语言。

开发人员通常使用多种编程语言,而不仅仅是一种。当没有约定,也没有跨语言的通用标准化时,从一种语言切换到另一种语言确实很麻烦。

我认为,语言之间的语法差异应仅在必要时,但不应超过此。


我喜欢语言“敢于”有所不同的时候。老实说,有太多的“ C语法”语言,并且C语言是很久以前设计的。有50多年的语言经验。投票
user2864740

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

4

文档

Swift的增减运算符是在Swift的早期开发中添加的,是从C继承下来的。这些添加时没有过多考虑,并且从那时起就没有考虑太多。本文档对它们进行了全新的介绍,并最终建议我们将它们完全删除,因为它们会造成混淆并且不承担重量。


换句话说,此操作太昂贵了,无法使用?
Oleg Gordiichuk

2
github.com/apple/swift-evolution/blob/master/proposals/…在这里您可以阅读有关它的信息,但这并不是因为它昂贵,而是因为语言设计。
丹尼尔·纳吉

因此,随着我安德森·斯威夫特(Andersen Swift)放弃对C样式功能的支持
Oleg Gordiichuk

2
@OlegGordiichuk好吧,我想说的是,他们想强调一下,Swift不是与Objective-C不同的C的超集。
丹尼尔·纳吉

1
@mah你所说的很多话根本没有任何意义。“不面向现有开发人员”以什么方式?是否以Java不面向PHP开发人员的方式?“面向那些可能不愿意成为开发人员的人”?是的,因为那里的所有非开发人员都在用面向协议的编程和泛型来解决问题。只需看一下SO,“一种实现良好设计的方法”,您就会发现,没有一种编程语言可以“实现良好设计”。
Fogmeister '16

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

由于您从未真正在Swift中使用过指针,因此我认为删除++and --运算符是很有意义的。但是,如果您不能没有,可以将以下Swift 5+运算符声明添加到项目中:

@discardableResult
public prefix func ++<T: Numeric>(i: inout T) -> T {
    i += 1
    return i
}

@discardableResult
public postfix func ++<T: Numeric>(i: inout T) -> T {
    defer { i += 1 }
    return i
}

@discardableResult
public prefix func --<T: Numeric>(i: inout T) -> T {
    i -= 1
    return i
}

@discardableResult
public postfix func --<T: Numeric>(i: inout T) -> T {
    defer { i -= 1 }
    return i
}

-3

在Swift 4.1中,可以通过以下方式实现:



    prefix operator ++
    postfix operator ++
    extension Int{
        static prefix func ++(x: inout Int)->Int{
            x += 1
            return x
        }
        static postfix func ++(x: inout Int)->Int{
            x += 1
            return x-1
        }
    }
    //example:
    var t = 5
    var s = t++
    print("\(t) \(s)")


请注意,尽管该解决方案与本文中的先前解决方案相似,但它们在Swift 4.1中不再起作用,此示例可以。还要注意,上面提到的任何人都说+ =是++的替代品,只是不完全理解该运算符,因为++与赋值组合实际上是两个操作,因此是捷径。在我的示例中:var s = t++有两件事:将t的值分配给s,然后递增t。如果++早于++,则是相同的两个操作,但顺序相反。在我看来,Apple关于为何删除此运算符的理由(在先前的回答中提到)不仅是错误的理由,而且我认为这是一个谎言,真正的原因是他们无法让编译器来处理它。在以前的版本中给他们带来了麻烦,因此他们放弃了。“太复杂,难以理解的运算符,因此被删除”的逻辑显然是一个谎言,因为Swift包含的运算符要复杂得多,而有用的运算符却没有删除。而且,绝大多数编程语言都具有它。JavaScript,C,C#,Java,C ++等。程序员乐于使用它。谁很难理解这个运算符,

Swift背后的策略很简单:Apple认为程序​​员很笨,因此应该相应地对待它。

事实是,2014年9月推出的Swift到现在应该在其他地方了。其他语言的成长速度更快。

我可以列举该语言中的许多主要错误,从严重的错误开始,例如按值粘贴而不是按引用粘贴的数组,再到令人讨厌的错误:可变参数函数不能接受数组,这是其背后的全部思想。我认为苹果的员工甚至不被允许使用其他语言(例如Java),因此他们甚至都不知道苹果落后了几年。苹果本可以采用Java作为语言,但如今,挑战不是技术,而是自我。如果他们本来可以打开IntelliJ来编写一些Java,那么他们肯定会关闭他们的业务了解,因为这时他们永远也不会追上来。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.