Swift 1.2中的@noescape属性

Dán*_*agy 75 attributes closures swift

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

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

在我的理解中,在此之前,我们可以使用[weak self]不让闭包有一个强引用,例如它的类,并且self可以是nil或闭包执行时的实例,但现在,@noescape意味着闭包永远不会被执行如果该课程被去除了资格.我理解正确吗?

如果我是正确的,为什么我会使用@noescape一个常规函数的闭包,当它们的行为非常相似时?

rin*_*aro 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 () -> ()) {}
Run Code Online (Sandbox Code Playgroud)

添加@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
Run Code Online (Sandbox Code Playgroud)

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

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

  • 对我来说关键是:"`@ noescape`保证闭包不会......异步使用".这意味着您无法将其用于触发异步的网络代码. (5认同)

Mic*_*ray 28

考虑它的一种方法是,@ noescape块内的每个变量都不需要强(不仅仅是自我).

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

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

变量现在甚至不需要零参考弱变量(比不安全的指针更昂贵),因为它们将保证在块的生命周期中"活着".

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


Dav*_*ine 8

(参考迈克尔格雷的上述答案.)

不确定这是否为Swift特别记录,或者即使Swift编译器也能充分利用它.但是,如果编译器知道被调用的函数不会尝试在堆中存储指向该实例的指针,那么它是标准的编译器设计,用于为堆栈上的实例分配存储,如果函数尝试这样做,则发出编译时错误.

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

同样,Swift团队是否必须声明Swift编译器的给定版本是否必须由其进行声明,或者您必须在开源时阅读源代码.从上面关于"小优化"的引用中,它听起来要么不是,要么Swift团队认为它是"次要的".我认为这是一个重要的优化.

据推测,该属性存在,以便(至少在将来)编译器将能够执行此优化.