@ noescape 属性在 Swift 1.2中

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

这表明 参数只被调用(或作为 @ 调用中的 noescape 参数) ,这意味着它不能 比电话寿命长。

据我所知,在此之前,我们可以使用 [weak self]不让闭包有一个强引用,例如它的类,并且在执行闭包时,self 可以是 nil 或者实例,但是现在,@noescape意味着如果类被去初始化,闭包将永远不会被执行。我没理解错吧?

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

15024 次浏览

@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

另外,从编译器的角度来看(如 释放通知书中所记录的) :

这可以实现一些小的性能优化。

考虑这个问题的一种方法是,@noescape 块中的每个变量都不需要是 Strong (不仅仅是 self)。

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

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

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

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

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

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

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

同样,Swift 编译器的特定版本是否真的需要 Swift 团队说明,或者当他们开放源代码时你必须阅读源代码。从上面关于“次要优化”的引用来看,它听起来要么不是,要么 Swift 团队认为它是“次要的”。我认为这是一个重要的优化。

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