Swift 1.2 中的@noescape 属性

@noescape attribute in Swift 1.2

Swift1.2 中有一个新属性,函数中有闭包参数,正如文档所说:

This indicates that the parameter is only ever called (or passed as an @ noescape parameter in a call), which means that it cannot outlive the lifetime of the call.

据我了解,在此之前,我们可以使用 [weak self] 来不让闭包对例如它的 class 和 self 可以是 nil 或者执行闭包时的实例,但是现在, @noescape 意味着如果 class 被取消初始化,闭包将永远不会被执行。我理解正确吗?

如果我是对的,当它们的行为非常相似时,为什么我要使用 @noescape 闭包代替常规函数?

@noescape可以这样使用:

func doIt(code: @noescape () -> ()) {
    /* what we CAN */

    // just call it
    code()
    // pass it to another function as another `@noescape` parameter
    doItMore(code)
    // capture it in another `@noescape` closure
    doItMore {
        code()
    }

    /* what we CANNOT do *****

    // pass it as a non-`@noescape` parameter
    dispatch_async(dispatch_get_main_queue(), code)
    // store it
    let _code:() -> () = code
    // capture it in another non-`@noescape` closure
    let __code = { code() }

    */
}

func doItMore(code: @noescape () -> ()) {}

添加@noescape保证闭包不会存储在某处、稍后使用或异步使用。

从调用者的角度来看,不需要关心捕获变量的生命周期,因为它们在被调用函数中使用或根本不使用。作为奖励,我们可以使用隐式 self,从而避免输入 self..

func doIt(code: @noescape () -> ()) {
    code()
}

class Bar {
    var i = 0
    func some() {
        doIt {
            println(i)
            //      ^ we don't need `self.` anymore!
        }
    }
}

let bar = Bar()
bar.some() // -> outputs 0

此外,从编译器的角度来看(如 release notes 中所述):

This enables some minor performance optimizations.

一种思考方式是,@noescape 块内的每个变量都不需要是 Strong(不仅仅是 self)。

也有可能进行优化,因为一旦分配了一个变量然后将其包装在一个块中,它就不能正常地在函数结束时被释放。所以必须分配在Heap上,用ARC解构。在 Objective-C 中,您必须使用“__block”关键字来确保以块友好的方式创建变量。 Swift 会自动检测到,所以不需要关键字,但成本是一样的。

如果变量被传递给@nosecape 块,那么它们可以是堆栈变量,并且不需要 ARC 来释放。

变量现在甚至不需要是零引用弱变量(比不安全指针更昂贵),因为它们将保证在块的生命周期内是 "alive"。

所有这些都会产生更快、更优化的代码。并减少使用 @autoclosure 块的开销(非常有用)。

(参考上面迈克尔·格雷的回答。)

不确定这是否专门针对 Swift 进行了记录,或者甚至 Swift 编译器是否充分利用了它。但如果编译器知道被调用的函数不会尝试将指向该实例的指针存储在堆中,则标准编译器设计会在堆栈上为实例分配存储空间,如果函数尝试这样做,则会发出编译时错误.

这在传递非标量值类型(如枚举、结构、闭包)时特别有用,因为复制它们可能比简单地将指针传递到堆栈要昂贵得多。分配实例的成本也大大降低(一条指令与调用 malloc() 相比)。因此,如果编译器可以进行此优化,那将是双赢的。

同样,Swift 编译器的给定版本是否确实需要由 Swift 团队声明,否则您必须在他们打开时阅读源代码-来源它。从上面关于 "minor optimization" 的引述来看,这听起来要么不是,要么 Swift 团队认为它是 "minor"。我认为这是一个重要的优化。

推测该属性存在,以便(至少在将来)编译器将能够执行此优化。