- @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