Swift 1.2 中的 @noescape 属性
Posted
技术标签:
【中文标题】Swift 1.2 中的 @noescape 属性【英文标题】:@noescape attribute in Swift 1.2 【发布时间】:2015-04-10 05:38:51 【问题描述】:Swift 1.2 中有一个新属性,函数中有闭包参数,正如文档所述:
这表明 参数只被调用(或作为 @ noescape 参数),这意味着它不能 超过调用的生命周期。
据我了解,在此之前,我们可以使用[weak self]
来不让闭包对例如它的类,self 可以是 nil 或执行闭包时的实例,但现在,@noescape
意味着如果类被取消初始化,闭包将永远不会被执行。我理解正确吗?
如果我是对的,当它们的行为非常相似时,为什么我要使用常规函数的 @noescape
闭包?
【问题讨论】:
【参考方案1】:@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 中所述):
这可以实现一些小的性能优化。
【讨论】:
对我来说关键位:“@noescape
保证闭包不会...异步使用”。这意味着您不能将它与触发异步的网络代码一起使用。【参考方案2】:
一种思考方式是,@noescape 块内的每个变量都不需要是强的(不仅仅是自我)。
还有一些优化是可能的,因为一旦分配了一个变量,然后包装在一个块中,它就不能在函数结束时正常释放。所以必须在Heap上分配,使用ARC解构。在 Objective-C 中,您必须使用“__block”关键字来确保以块友好的方式创建变量。 Swift 会自动检测到,因此不需要关键字,但成本是一样的。
如果变量被传递给 @nosecape 块,那么它们可以是堆栈变量,并且不需要 ARC 来解除分配。
变量现在甚至不需要是零引用的弱变量(这比不安全的指针更昂贵),因为它们将保证在块的生命周期内是“活动的”。
所有这些都会产生更快、更优化的代码。并减少使用 @autoclosure 块的开销(非常有用)。
【讨论】:
您能否提供一些参考,我在哪里可以阅读您在此处所说的内容,我会对这些细节非常感兴趣。【参考方案3】:(参考上面迈克尔·格雷的回答。)
不确定这是否是专门为 Swift 记录的,或者即使 Swift 编译器也充分利用了它。但是,如果编译器知道被调用的函数不会尝试将指向该实例的指针存储在堆中,并且如果函数尝试这样做,则会发出编译时错误,则为堆栈上的实例分配存储是标准的编译器设计.
这在传递非标量值类型(如枚举、结构、闭包)时特别有用,因为复制它们可能比简单地传递指向堆栈的指针要昂贵得多。分配实例的成本也大大降低(一条指令与调用 malloc() 相比)。因此,如果编译器可以进行这种优化,那将是双赢的。
同样,Swift 团队必须说明给定版本的 Swift 编译器是否确实如此,否则您必须在他们开源时阅读源代码。从上面关于“次要优化”的引用中,听起来要么没有,要么 Swift 团队认为它是“次要的”。我认为这是一项重大的优化。
推测该属性的存在是为了(至少在将来)编译器将能够执行此优化。
【讨论】:
以上是关于Swift 1.2 中的 @noescape 属性的主要内容,如果未能解决你的问题,请参考以下文章
为啥 @noescape 在需要时不会自动应用于 Swift 闭包?
Swift:无法使用类型为“(@noescape(Int)抛出-> Bool)”的参数列表调用“过滤器”