Escaping and Nonescaping Closures in Swift3

本文翻译自这里

函数与闭包在Swift中作为一等公民,可以存储,当作参数传递,并且把它们看待成其他的对象或者类型。通常我们将闭包当作发送API完成后的回调。

在Swift3中,当你将闭包当作一个函数的参数时,会有一个新的建议:编译器将闭包默认为non-escaping。下面看看non-escapingeascaping的区别。

Non-Escaping Closures

non-escaping的生命周期:

1.将闭包传递给函数
2.函数执行闭包(或不执行)
3.函数返回

Escaping and Nonescaping Closures in Swift3_第1张图片
non-escaping

这个闭包并没有“逃逸(escape)”到函数体外。当函数结束时,传递的闭包离开函数作用域,并且没有其他的引用指向该闭包。

如果考虑到内存的持有和释放平衡,这个闭包的引用计数在函数结束时和开始时是一样的。

Escaping Closures

现在可以猜到escaping closure是什么意思。在函数内,你可以一直运行闭包(或者不);这里有几种方法来让闭包逃逸出函数体:

  • 异步操作:如果在一个异步队列中执行闭包,那么这个队列会一直持有这个闭包。你无法确定闭包何时被执行,并且也无法保证在函数返回前结束闭包。

  • 存储:将闭包存储为全局变量、属性、或者任何其他的存储方式。

Escaping and Nonescaping Closures in Swift3_第2张图片
escaping

Escaping and Non-Escaping in Swift 3

在Swift1和2中,闭包参数默认为escaping。如果能够确保闭包没有逃逸出函数体,你可以使用@noescape修饰它。

在Swift3中有些不同,参数闭包默认修饰为non-escaping

如果闭包修饰为non-escaping,这里有一些潜在的优化。因为闭包不会逃逸,编译器可以将闭包的存储和调用优化。

经常碰到的情况就是闭包中持有self的时候:

class ClassA {
     // takes a closure (non-escaping by default) 
     func someMethod(closure: () -> Void) {  
         // secret stuff 
    }
}
class ClassB { 
    let classA = ClassA() 
    var someProperty = "Hello" 
    
    func testClosure() {
         classA.someMethod {
            // self is captured! 
            someProperty = "Inside the closure!" 
        } 
    }
}

当调用someMethod的时候,记住somePropertyClassB的成员属性。只有闭包为逃逸闭包的时候才必须使用self,而这段代码在Swift3中运行不会有任何问题。
这个闭包仍然会截获self,但是因为闭包在函数结束后就会释放,所以编译器会知道没有发生循环引用。
如果将代码改成如下这样:

func someMethod(closure: @escaping () -> Void) {
     // secret stuff
}

现在这变成另外一种情况,当调用这个方法并且提供一个有引用指向的闭包,在闭包内必须明确使用self来提醒自己这个闭包截获了当前的self

最后

在Swift3中,闭包参数默认为non-escaping,根据自己的需求使用@escaping。非逃逸闭包当做参数传递时,在函数返回之前闭包必须执行完。

你可能感兴趣的:(Escaping and Nonescaping Closures in Swift3)