1. 程式人生 > >Swift中的逃逸閉包的理解

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是因為閉包最後才被執行……