Swift 1.2中的@noescape属性


75

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

这表明该参数只能被调用(或在调用中作为@ noescape参数传递),这意味着它不能超过调用的生命周期。

以我的理解,在此之前,我们可以[weak self]不让闭包强烈引用其类,而self可以为nil或执行闭包时的实例,但是现在,这@noescape意味着闭包将永远不会执行如果该课程取消了初始化。我理解正确吗?

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

Answers:


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

同样,从编译器的角度来看(如发行说明中所述):

这样可以进行一些次要的性能优化。


5
对我来说,关键一点是:“@noescape确保不会以异步方式使用闭包”。这意味着您不能将其与引发异步的联网代码一起使用。
戴维·詹姆斯

28

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

还有一些优化的可能,因为一旦分配了一个变量然后将其包装在一个块中,通常就不能在函数的末尾对其进行重新分配。因此,必须在堆上分配它并使用ARC进行解构。在Objective-C中,必须使用“ __block”关键字来确保以块友好方式创建变量。Swift会自动检测到该关键字,因此不需要关键字,但代价是相同的。

如果将变量传递给@nosecape块,则它们可以是堆栈变量,并且不需要ARC来取消分配。

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

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


您能提供一些参考资料吗,我可以在这里阅读您所说的内容,我会对这些细节非常感兴趣。
丹尼尔·纳吉

8

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

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

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

同样,给定版本的Swift编译器是否确实需要由Swift团队说明,或者您必须在开放源代码时阅读源代码。从上面关于“次要优化”的引用中,听起来好像没有,或者Swift团队认为它是“次要”。我认为这是一个重大的优化。

大概该属性在那里,以便(至少在将来)编译器将能够执行此优化。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.