Swift3:@escaping

作者: 请叫我小陈陈 | 来源:发表于2016-10-17 15:58 被阅读3761次
    • @escaping

    在Swift3中,闭包默认是非逃逸的。在Swift3之前,事情是完全相反的:那时候逃逸闭包是默认的,对于非逃逸闭包,你需要标记@noescaping。Swift3的行为更好。因为它默认是安全的:如果一个函数参数可能导致引用循环,那么它需要被显示地标记出来。@escaping标记可以作为一个警告,来提醒使用这个函数的开发者注意引用关系。非逃逸闭包可用被编译器高度优化,快速的执行路径将被作为基准而使用,除非你在有需要的时候显式地使用其他方法。

    @escaping标明这个闭包是会“逃逸”,通俗点说就是这个闭包在函数执行完成之后才被调用。为了体现@escaping的作用,我们在之前先做一个铺垫:

    func doWork(block:()->()) {
        print("header")
        block()
        print("footer")
    }
    doWork {
        print("work")
    }
    //控制台打印的消息如下:
    //header
    //work
    //footer
    

    对于上述的block调用是同步行为。我们修改一下代码,将block放到一个异步操作中,让它在doWork返回后被调用。这个时候我们就需要用@escaping标记表明这个闭包是会“逃逸”的。

    func doWorkAsync(block: @escaping () -> ()) {
        DispatchQueue.main.async { 
            block()
        }
    }
    

    没有逃逸的闭包的作用域是不会超过函数本身的,所以说我们不需要担心在闭包内持有self。逃逸的闭包就不同了,因为需要确保闭包内的成员依然有效,如果在闭包内引用self以及self的成员的话,就要考虑闭包内持有self的情况了。

    class S {
        var foo = "foo"
        
        func method1() {
            doWork {
                print(foo)
            }
            foo = "bar"
        }
        
        func method2() {
            doWorkAsync {
                print(self.foo)
            }
            foo = "bar"
        }
    func method3() {
            doWorkAsync {
                [weak self] _ in
                print(self?.foo)
            }
            foo = "bar"
        }
    
    }
    S().method1()// foo
    S().method2()// bar
    S().method3()// nil
    

    method1不需要考虑self .foo的持有情况,而method2需要考虑,我们让闭包持有了self,打印的值就是foo赋值之后的内容bar,如果我们不希望闭包内持有self的话,可以使用[weak self]的方法来表示. method3就是这样,在闭包执行的时候已经没有了对实例对象的引用,所有说输出是nil。

    • weak 和 unowned
      上面我用的是 [weak self] ,如果用[unowned self]表示的话,method3就需要稍做修改:
    func method3() {
            doWorkAsync {
                [unowned self] _ in
                print(self.foo)
            }
            foo = "bar"
        }
    

    这两者都是用来防止循环引用的,但是还是有一点小小的区别:unowned 有点像oc里面的unsafe_unretained,而weak就是以前的weak。对于这两者的使用,不能说用哪一个要好一点,要视情况而定。用unowned的话,即使它原来的引用的内容被释放了,它仍然会保持对被已经释放了的对象的一个引用,它不能是Optional也不能是nil值,这个时候就会出现一个问题,如果你调用这个引用方法或者访问成员属性的话,就会出现崩溃。而weak要稍微友善一点,在引用的内容被释放之后,会自动将weak的成员标记为nil。有人要说,既然这样,那我全部使用weak。但是在可能的情况下,我们还是应该倾向于尽量减少出现Optional 的可能性,这样有助于代码的简化。Apple给我们的建议是如果能够确定访问时不会被释放的话,尽量用unowned,如果存在被释放的可能性的话,就用weak。

    • 在Playground里面进行异步操作
      上面的代码如果你在Playground里面运行的话,你会发现method2,method3方法里面的回调不会走。不会走的原因大概是来不及走,三个函数一执行完,程序就终止了,异步来不及执行。怎么来解决这个问题呢?在Xcode 8 里面我们可以import PlaygroundSupport,在调用方法前设置PlaygroundPage.current.needsIndefiniteExecution = true就好了。
    PlaygroundPage.current.needsIndefiniteExecution = true
    S().method1()// foo
    S().method2()// bar
    S().method3()// nil
    

    参考资料:http://stackoverflow.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground

    相关文章

      网友评论

      • sea7reen:非常有用
      • 星火燎原:我只回好贴
      • 我想走走:playground指的是什么?大神
        请叫我小陈陈:我打错单词了,应该是Playground 。
        Playground是苹果xcode6自带的一种快速学习代码的模式,xcode启动的时候,就会显示一个界面。上面第一项就是“Get started with a playground”。或者是项目里面新建文件的时候选择iOS中的Source下面的Playground。
      • 托尼的夏天:谢谢大神

      本文标题:Swift3:@escaping

      本文链接:https://www.haomeiwen.com/subject/tugayttx.html