了解优先组在Swift 3.1中的赋值

时间:2017-07-17 00:23:44

标签: swift swift3 operators

Swift允许定义我们自己的优先级组以用于我们的自定义运算符。有一件事我很难理解,那就是布尔assignment属性。

来自 Swift编程语言(Swift 3.1)

  

优先级组的分配指定在包含可选链接的操作中使用的运算符的优先级。设置为true时,相应优先级组中的运算符在可选链接期间使用与标准库中的赋值运算符相同的分组规则。否则,当设置为false或省略时,优先级组中的运算符遵循与不执行赋值的运算符相同的可选链接规则。

它没有说明assignment属性的确切行为。可选链接中的赋值运算符和非赋值运算符之间有什么区别?

我将++=(表现得像+=)和++(表现得像+)运算符与AssignmentTesting结构一起定义以进行测试。

precedencegroup AssignmentTrue {
    assignment: true
}

precedencegroup AssignmentFalse {
    assignment: false
}

infix operator ++=: AssignmentTrue
infix operator ++: AssignmentFalse

extension Int {
    static func ++= (left: inout Int, right: Int) {
        left += right
    }

    static func ++ (left: Int, right: Int) -> Int {
        return left + right
    }
}

struct AssignmentTesting { var number = 0 }

var assignmentTesting: AssignmentTesting? = AssignmentTesting()
assignmentTesting?.number ++= 3 // assigns 0 + 3 to assignmentTesting.number
assignmentTesting!.number ++ 5 // returns 3 + 5
assignmentTesting?.number // == 3

assignment: trueassignment: false如何影响这些行为?任何解释都受到欢迎。

3 个答案:

答案 0 :(得分:3)

您的示例代码很好地描述了差异:

// assigns (assignmentTesting!.number + 3) to assignmentTesting!.number when assignmentTesting != nil
// Do nothing when assignmentTesting == nil
assignmentTesting?.number ++= 3

// assigns (assignmentTesting!.number + 3) to assignmentTesting!.number when assignmentTesting != nil
// Crashing when assignmentTesting == nil
assignmentTesting!.number ++= 3

// Does not compile
//assignmentTesting?.number ++ 5

// returns (assignmentTesting!.number + 5) when assignmentTesting != nil
// Crashing when assignmentTesting == nil
assignmentTesting!.number ++ 5

++=是赋值运算符时,像assignmentTesting?.number ++= 3这样的可选链接非常类似于:

(assignmentTesting != nil) ? (assignmentTesting!.number ++= 3) as Void?  : nil

但是当++不是赋值运算符时,assignmentTesting?.number ++ 5不是Swift中的有效表达式,并且不能用作:

(assignmentTesting != nil) ? (assignmentTesting!.number ++ 5) as Int? : nil

答案 1 :(得分:0)

区别的简单解释是,如果在可选链接期间使用不可分配的运算符,则无法编译代码。

在您的示例中,您设置了assignmentTesting?.number ++= 3 ++=尝试与+=行为相同的assignment: false。但是,作为++=,优先级组中的运算符遵循与不执行赋值的运算符相同的可选链接规则。分配没有被执行,因此编译器会抱怨。

因此,您需要在AssignmentPrecedence中添加infix operator ++=: AssignmentPrecedence ,以便在可选链接期间将其指定为可分配。

precedencegroup AssignmentTrue {
    assignment: true
}
infix operator ++=: AssignmentTrue

foo?.bar += 2

<强>更新

您的示例实际上是自我解释的,并且您还指出操作员标记的分配被折叠到可选链中,允许foo?(.bar += 2)作为(foo?.bar) += 2工作,而不是无法键入 - 检查为assignmentTesting。此行为将传递给赋值:来自SE-0077的优先组上的true。

  • 如果您将++=声明为可选,但不在AssignmentPrecedence组中插入assignmentTesting?.number += 3,则编译器会将该语句设为(assignmentTesting?.number)。它不知道nil是否会返回assignmentTesting。因此无法编译。

  • 如果您将++=声明为AssignmentPrecedence组中的assignmentTesting?.number += 3assignmentTesting?(.number += 3)组中的number += 3,则assignmentTesting可以assignmentTesting!.number += 3assignmentTesting SE-0077。它在编译时经过类型检查并证明是正确的。该语句将通过,因为nil将优先于可选链接。

  • 如果强制解包assignmentTesting,则语句为{{1}}。由于您确定{{1}}不会{{1}},编译器不会抱怨。但是,如果在运行时期间{{1}}未能成功解包,则会触发运行时错误。

答案 2 :(得分:0)

一个二进制运算符,甚至是一个不执行不执行的运算符,也可以包含在带有assignment: true的优先级组中。在这种情况下,如果其中一个操作数使用可选链接(并且不会失败,因为它是 nil ),则运算符将对隐藏在可选后面的值进行操作。返回的结果仍然是可选的。

对于assignment: false,运算符只会将操作数视为可选值,而不是所需的值类型,并且会出现错误。

在您的代码中,替换以下行:

 assignmentTesting!.number ++ 5

所在行:

assignmentTesting?.number ++ 5

如果将AssignmentFalse设置为运算符++的优先级组,则会出现错误。如果设置AssignmentTrue,它将起作用,将5附加到隐藏在Optional后面的值3中,然后返回Optional(8)