Swift 1.2中有一个带有闭包参数的新属性,正如文档中所说:
这表明了 参数只被调用(或传递为 @ 调用中的noescape参数),这意味着它不能 比电话的生命周期更长。
根据我的理解,在此之前,我们可以使用[weak self]
来禁止封闭对它的类和self在执行闭包时可以是nil或实例,但是现在,@noescape
意味着如果类被去除了化身,闭包永远不会被执行。我理解正确吗?
如果我是正确的,为什么我会使用@noescape
关闭普通函数的闭包,当它们的行为非常相似时?
答案 0 :(得分:143)
@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中所述):
这可以实现一些小的性能优化。
答案 1 :(得分:28)
考虑它的一种方法是,@ noescape块内的每个变量都不需要强(不仅仅是自我)。
也有可能进行优化,因为一旦分配了一个变量然后包含在一个块中,它就不能正常地在函数末尾解除分配。所以它必须在堆上分配并使用ARC来解构。在Objective-C中,您必须使用“__block”关键字来确保以块友好的方式创建变量。 Swift会自动检测到这一点,因此不需要关键字,但成本是相同的。
如果将变量传递给@nosecape块,则它们可以是堆栈变量,并且不需要ARC来解除分配。
变量现在甚至不需要零参考弱变量(比不安全指针更昂贵),因为它们将保证在块的生命周期中“活着”。
所有这些都会带来更快,更优化的代码。并减少了使用@autoclosure块的开销(这非常有用)。
答案 2 :(得分:8)
(参考迈克尔格雷的上述答案。)
不确定这是否为Swift特别记录,或者即使Swift编译器也能充分利用它。但是,如果编译器知道被调用的函数不会尝试在堆中存储指向该实例的指针,那么它的标准编译器设计就会为堆栈上的实例分配存储空间,并且如果函数发出编译时错误试图这样做。
这在传递非标量值类型(如枚举,结构,闭包)时尤其有用,因为复制它们可能比简单地将指针传递给堆栈要昂贵得多。分配实例的成本也要低得多(一条指令与调用malloc()相比)。因此,如果编译器可以进行此优化,那么它就是双赢。
同样,Swift团队是否必须提供Swift编译器的特定版本,或者您必须在开源时阅读源代码。从上面关于"次要优化"的引用中,它听起来要么不是,要么Swift团队认为它是次要的"。我认为这是一个重要的优化。
据推测,该属性存在,以便(至少在将来)编译器能够执行此优化。