Swift 1.2中的@noescape属性
在Swift 1.2中有一个新的属性,在函数中有闭包参数,正如文档中所说:
这表明该参数只能被调用(或者在一个调用中作为@ noescapeparameter passing),这意味着它不能超过调用的生命周期。
根据我的理解,在此之前,我们可以使用[weak self]
不让闭包有强烈的引用,比如它的类,而self可以是nil或闭包被执行时的实例,但是现在@noescape
意味着如果这个阶级失败了,封闭将永远不会被执行。 我理解正确吗?
如果我是正确的,为什么我会使用一个普通函数的@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
保证闭包不会被存储在某个地方,以后使用,或者asynchronous使用。
从调用者的angular度来看,不需要关心捕获的variables的生命周期,因为它们在被调用的函数中使用,或根本不用。 作为一个奖励,我们可以使用一个隐含的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
另外,从编译器的angular度来看(如发行说明中所述 ):
这使得一些次要的性能优化。
考虑这一点的一个方法是@noescape块内的EVERYvariables不需要强(不仅仅是自身)。
也有可能的优化,因为一旦一个variables被分配,然后包装在一个块中,它不能通常在函数结束时被释放。 所以它必须在堆上分配,并使用ARC来解构。 在Objective-C中,你必须使用“__block”关键字来确保variables是以块友好的方式创build的。 Swift会自动检测到这个关键字是不需要的,但是成本是一样的。
如果variables被传递给一个@nosecape块,比它们可以是堆栈variables,并且不需要ARC去释放。
variables现在甚至不需要零参考弱variables(它比不安全的指针更昂贵),因为它们将在块的生命周期中保证“活着”。
所有这些都会产生更快,更优化的代码。 并减less使用@autoclosure块的开销(这是非常有用的)。
(关于上面的Michael Gray的回答)
不知道这是否特别logging了Swift,或者即使Swift编译器充分利用它。 但是,如果编译器知道被调用的函数不会尝试在堆中存储指向该实例的指针,并且在函数试图这样做时发出编译时错误,那么标准的编译器devise就是为堆栈上的实例分配存储空间。
这在传递非标量值types(如枚举,结构,闭包)时尤其有利,因为复制它们可能比将指针传递给堆栈要昂贵得多。 分配实例也明显要便宜(一条指令与调用malloc())。 所以如果编译器可以做这个优化的话就是双赢了。
同样,Swift编译器的给定版本实际上是否必须由Swift团队来陈述,或者当他们开放源代码时,你必须阅读源代码。 从上面关于“小优化”的引用来看,这听起来好像没有,或者Swift团队认为它是“小”。 我会认为这是一个重大的优化。
据推测该属性是在那里(至less在将来)编译器将能够执行此优化。