Swift中的逃逸閉包的理解
阿新 • • 發佈:2019-01-06
逃逸閉包的書面定義
一個傳入函式的閉包如果在函式執行結束之後才會被呼叫,那麼這個閉包就叫做逃逸閉包。
對定義的理解
通過定義我們知道,逃逸閉包首先是一個閉包(感覺有點廢話),但是逃逸閉包又不是普通的閉包,因為它會在函式結束後才執行(這是特點)。
什麼閉包會在函式執行之後才執行呢?
很多啟動非同步操作的函式接受一個閉包引數作為 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是因為閉包最後才被執行……