Swift中的逃逸闭包的理解

逃逸闭包的书面定义

一个传入函数的闭包如果在函数执行结束之后才会被调用,那么这个闭包就叫做逃逸闭包。

对定义的理解

通过定义我们知道,逃逸闭包首先是一个闭包(感觉有点废话),但是逃逸闭包又不是普通的闭包,因为它会在函数结束后才执行(这是特点)。

什么闭包会在函数执行之后才执行呢?

很多启动异步操作的函数接受一个闭包参数作为 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:内部执行someFunctionWithEscapingClosuresomeFunctionWithNonescapingClosure,即期望内部会利用两个尾随闭包对x进行赋值
*/
instance.doSomething()
print(instance.x)
// 打印出 "200"

completionHandlers.first?()
print(instance.x)
// 打印出 "100"

看了这段代码的输出结果,我一开始是很诧异的,因为对于逃逸闭包的不理解,所以很难理解为什么第二次打印会输出的是100?

对于代码执行结果的解释

因为逃逸闭包是在函数执行之后才会执行,所以可以这么理解:

  1. 我创建了一个类的对象instance
  2. 对象中初始化了一个x = 10
  3. 利用对象执行了函数doSomething
  4. 函数内部先调用了全局函数someFunctionWithEscapingClosure该函数传入了尾随闭包{ self.x = 100 },期望修改instance对象中的x值为100(但是此时并没有执行这个包含了赋值语句的闭包)
  5. 函数内部进一步调用全局函数someFunctionWithNonescapingClosure该函数传入了尾随闭包{ x = 200 },期望修改instance对象中的x值为200(因为此时全局函数someFunctionWithNonescapingClosure内部执行这个包含了赋值语句的闭包,所以x的值由10变为了200)
  6. 输出(显然结果为200)
  7. 查找全局数组completionHandlers,找到里面第一个元素,显然找到的是在someFunctionWithEscapingClosure函数中添加的闭包{ self.x = 100 },此时才通过全局数组的查询找出闭包并执行,于是x此时才被赋值为100(典型的someFunctionWithEscapingClosure函数执行完毕后才执行闭包{ self.x = 100 },于是这个在函数之后最后才执行到的闭包,就是符合定义的逃逸闭包了。

结论

逃逸闭包将在函数执行之后执行,于是这段代码最后输出为100是因为闭包最后才被执行……

发布了24 篇原创文章 · 获赞 1 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/shenjie_xsj/article/details/78835531
今日推荐