逃逸闭包的书面定义
一个传入函数的闭包如果在函数执行结束之后才会被调用,那么这个闭包就叫做逃逸闭包。
对定义的理解
通过定义我们知道,逃逸闭包首先是一个闭包(感觉有点废话),但是逃逸闭包又不是普通的闭包,因为它会在函数结束后才执行(这是特点)。
什么闭包会在函数执行之后才执行呢?
很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类启动异步操作的函数会在异步操作开始之后(即“启动异步操作”的函数已经执行完毕)立刻返回,但是闭包直到异步操作结束后才会被调用(即“启动异步操作”函数执行完毕后才被调用)。这中情况很完美的符合了逃逸闭包的定义……
看一个例子:
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void)
{
completionHandlers.append(completionHandler)
}
上面这段代码定义了一个全局变量completionHandlers
这个全局变量是一个数组,内部专门用来存放没有参数、没有返回值的闭包,同时定义了函数someFunctionWithEscapingClosure
,这个函数接收了一个闭包,并将这个闭包添加到外部全局数组中。然后函数结束(注意:此时传入的闭包并没有执行,仅仅是添加到全局数组中)。
接下来完善一下上面的代码:
//定义一个存放闭包的全局数组
var completionHandlers: [() -> Void] = []
//定义一个接收闭包的函数
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void)
{
completionHandlers.append(completionHandler)
}
//定义另一个接收闭包的函数
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
/*
定义一个类:
初始化x值为10
通过调用上面定义的两个函数,使用尾随闭包的方式将实现"对x赋值"这么一个功能的闭包传入
*/
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
//创建类的对象
let instance = SomeClass()
/*
执行doSomething函数
PS:内部执行someFunctionWithEscapingClosure,someFunctionWithNonescapingClosure,即期望内部会利用两个尾随闭包对x进行赋值
*/
instance.doSomething()
print(instance.x)
// 打印出 "200"
completionHandlers.first?()
print(instance.x)
// 打印出 "100"
看了这段代码的输出结果,我一开始是很诧异的,因为对于逃逸闭包的不理解,所以很难理解为什么第二次打印会输出的是100?
对于代码执行结果的解释
因为逃逸闭包是在函数执行之后才会执行,所以可以这么理解:
- 我创建了一个类的对象
instance
- 对象中初始化了一个x = 10
- 利用对象执行了函数
doSomething
- 函数内部先调用了全局函数
someFunctionWithEscapingClosure
该函数传入了尾随闭包{ self.x = 100 }
,期望修改instance
对象中的x值为100(但是此时并没有执行这个包含了赋值语句的闭包) - 函数内部进一步调用全局函数
someFunctionWithNonescapingClosure
该函数传入了尾随闭包{ x = 200 }
,期望修改instance
对象中的x值为200(因为此时全局函数someFunctionWithNonescapingClosure
内部执行这个包含了赋值语句的闭包,所以x的值由10变为了200) - 输出(显然结果为200)
- 查找全局数组
completionHandlers
,找到里面第一个元素,显然找到的是在someFunctionWithEscapingClosure
函数中添加的闭包{ self.x = 100 }
,此时才通过全局数组的查询找出闭包并执行,于是x此时才被赋值为100(典型的someFunctionWithEscapingClosure
函数执行完毕后才执行闭包{ self.x = 100 }
,于是这个在函数之后最后才执行到的闭包,就是符合定义的逃逸闭包了。
结论
逃逸闭包将在函数执行之后执行,于是这段代码最后输出为100是因为闭包最后才被执行……