Escaping Closures - Swift

逃逸闭包和非逃逸闭包


逃逸闭包(escaping closure),什么是逃逸闭包?苹果官方给的定义是:当一个闭包作为一个参数传递给函数,但是它是在函数返回之后调用的,这时候,这个闭包就称为逃逸闭包。当你声明一个将闭包作为参数的函数时,你可以在参数的类型之前用 @escaping 来表明这个闭包是允许逃逸的。

闭包可以逃逸的一种方式是存储在函数之外定义的变量中。作为例子,许多启动异步操作的函数都将闭包参数放在异步执行完毕之后的操作中执行(completion handler)。该函数在开始操作后返回,但在操作完成之前不会调用闭包 — 闭包需要逃逸,稍后调用,举个例子:

1
2
3
4
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}

someFunctionWithEscapingClosure(_ :) 函数将闭包作为其参数,并将其添加到在函数外声明的数组中。如果你没有用 @escaping 来标记这个函数的参数,你会得到一个编译时的错误。

将一个闭包用 @escaping 来标记的话,意味着你需要在闭包中明确的引用自身 self ,例如,在下面的代码中,传递给 someFunctionWithEscapingClosure(_:) 的闭包是逃逸闭包,这就意味着它必须要明确的引用自身 self , 相比之下,传递给 someFunctionWithNonescapingClosure(_:) 的闭包是非逃逸闭包,这意味着它可以隐式的引用自身。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}

class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"

completionHandlers.first?()
print(instance.x)
// Prints "100"

非逃逸闭包示意图


非逃逸闭包示意图

逃逸闭包示意图


逃逸闭包示意图