2

考虑这段代码:

class Foo {
    var a: Int
    var b: Int

    init(a: Int, b: String?) throws {
        self.a = a

        guard self.a > 0 else {
            throw "Too little a!"
        }
        self.b = self.a
    }
}

extension String: Error {}

非常荒谬,但关键是它编译得很好。现在将警卫替换为:

guard b == nil || self.a > 0 else {

不是我们得到编译器错误!

错误:在所有成员初始化之前由闭包捕获的“自我”

我在任何地方都没有看到关闭。如果条件是复合表达式,编译器guard是否会将条件转换为闭包,从而引入错误(如果有闭包,这将是正确)?

错误或功能?

这是 Swift 3.0.2。

4

1 回答 1

2

正如 Martin在此 Q&A 中所解释的那样,问题在于该||运算符是使用@autoclosure第二个参数实现的,以允许进行短路评估(仅当左侧表达式评估为 时才需要评估右侧表达式false)。

因此在表达式

b == nil || self.a > 0

self.a > 0被隐式包裹在一个() -> Bool闭包中。这是有问题的,因为它需要捕获self, 以便a在应用闭包时可以访问它。

但是,在初始化程序中,Swift 严格限制了self在它完全初始化之前可以做什么。其中一个限制是无法被闭包捕获——这就是你得到编译器错误的原因。

虽然实际上,在完全初始化之前{ self.a > 0 }捕获闭包并没有什么问题,因为闭包所做的只是访问它,它已经被初始化了。因此,这实际上只是一个边缘案例(对此有一个开放的错误报告),我希望在该语言的未来版本中能够消除这种情况。selfa

在此之前,Martin在此 Q&A中展示的一种解决方案是使用临时局部变量来创建 的值的副本,避免在闭包self.a中捕获:self

class Foo {
    var a: Int
    var b: Int

    init(a: Int, b: String?) throws {

        // some computation meaning that a != self.a
        self.a = a * 42

        // temporary local variable to be captured by the @autoclosure.
        let _a = self.a
        guard b == nil || _a > 0 else {
            throw "Too little a!"
        }

        self.b = self.a
    }
}

extension String: Error {}

显然,这是假设,否则您可以只在条件中self.a != a引用a而不是。self.aguard

于 2017-03-28T21:26:24.673 回答