swiftSwiftSwift

Swift3:@escaping

2016-10-17  本文已影响3761人  请叫我小陈陈

在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。

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。

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

上一篇 下一篇

猜你喜欢

热点阅读