swift中使用@noescape的正确姿势

       如果大家有去看swift的API的话,会发现有些API使用了“@noescape”符号。比如,SequenceType协议的map

public func map<T>(@noescape transform: (Self.Generator.Element)throws -> T) rethrows -> [T]

        大家对map应该不陌生。它是用来对结构中的数据做映射。

        数组中,map的实现大致是这样:对数组的每个元素做映射,映射成其他的值然后生成一个新的数组返回给调用者

extension Array {
    func map<U>(transform: Element->U) -> [U] {
        var result: [U] = []
        result.reserveCapacity(self.count)
        for x in self {
            result.append(transform(x))
        }
        return result
    }
}

      大家应该也知道,闭包使用不当的时候,会引起引用循环。为了防止出现引用循环我们会在闭包中使用捕获列表。但是,我们在使用map的时候,即使调用了self也不会造成引用循环。我们几乎没有在map的闭包参数上使用捕获列表来防止引用循环(好吧,即使使用了捕获列表,其实也是没什么影响。就是多写了些冗余的代码而已)。

       请看如下栗子:

     【栗子1】


                                                                                                                   图1-1  

        从图1-1代码的运行结果中,我们可以看到,原来创建的ClassA实例被正常销毁了。代码并没有造成引用循环。

        为什么会这样呢?这是因为在map中,transform闭包仅仅是在调用map的时候生成的。它的生命周期就在map函数里,当map执行完,tranform闭包的生命周期就结束了。即使我们在map中使用了self,也不会造成引用循环。因为在这样的情况下,虽然闭包持有实例对象,但是闭包只被map参数持有。 如图1-2:


                                                                                                 图1-2

      noescape是非逃逸的意思。@noescape关键字代码中扮演了一个标注的作用:来说明一个闭包参数,该闭包参数与此API是同步的,它只在此API中被调用。只要该API运行结束,该闭包的生命周期就结束。也就是说,该闭包逃不出该API的手掌心。哈哈哈哈!它对编译器和API调用者来说:编译器会对代码做一些优化,而API调用者则可以放心大胆的使用该API,不用因为担心造成引用循环而去使用捕获列表。同时在其中调用实例变量或实例方法的时候可以不使用"self."

      氮素!如何使用这个@noescape标注,这是需要正确的姿势的!

      上面的论述,只有在闭包是临时创建,即没有被API外部的任何其他属性或全局变量持有的前提下才成立!!

     【API调用者的正确姿势】

     让我们来看看,当闭包参数被其他属性持有的情况下,使用map是什么结果。请看下面的代码:

     【栗子2】

   

                                                                                                 图 2-1

        可以看出,控制台并没有打印"ClassA 实例被销毁",也就是说,ClassA实例的deinit方法没有被调用。原先的ClassA实例没有被销毁。这段代码存在引用循环。(具体分析请见我上一节的博客)

        这是因为,传入map的闭包,被map外部的变量所持有。transform跟map再也不同步了。也不一定只有在map内部才会调用。如果我们高兴,可以在调用完a.handleDataArr()后接着调用a.transform。这个时候,该闭包是逃逸类型的闭包。它可以逃出map的手掌心。在这样的情况下,我们就必须要使用捕获列表,才能避免造成引用循环!

     【总结】: 我们在调用被@noescape标注的API的时候,如果我们传入的闭包被其他任何属性或者全局变量持有,我们一定要在闭包中使用捕获列表以避免产生引用循环;如果我们传入的只是临时创建的闭包(即没有被其他任何属性或全局变量所持有),则可以随意使用该API,不必使用捕获列表,调用属性或方法时也不必使用“self.”。


     【API创建者的正确姿势】

       由于编译器会优化带有@noescape的代码。所以,当我们自己写API的时候,如果有闭包参数,应该尽量写上@noescape。 如果你试图在该API中使用API外部变量来指向该闭包,编译器会报错:  "cannot assign value of type '@noescape Int -> Int' to type '(Int -> Int)?"

     

 

【栗子3】给自己的代码添加@noescape 


                                                                                                 图 3-1

最后,你get到@noescape使用的正确姿势了吗?^_^




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值