Swift文件Chapter 7 閉包
閉包是自包含的程式碼塊,可以在程式碼中被傳遞和使用。和Python中的匿名函式(Lambda)類似。
閉包表示式
閉包表示式是一種構建內聯閉包的方式,語法比較簡潔,並且提供了幾種優化的語法簡寫形式。
閉包表示式語法
{ (parameters) -> returnType in
statements
}
引數的型別也可以是inout
型別。
以排序演算法sorted(by:)
函式為例。當我們不使用預設排序方法排序的時候,需要傳入一個函式,函式的輸入是與陣列同類型的兩個值,輸出是一個布林值,如果第一個值出現在第二個值前面,返回true
,否則返回false
。
我們用一個String
陣列作為演示,那麼上面的函式可以寫成:
sortedStrings = Strings.sorted(by: { (s1: String, s2: String) -> return Bool in
s1 > s2
})
這個函式非常的短,可以改寫為一行程式碼:
sortedStrings = Strings.sorted(by: { (s1: String, s2: String) -> return Bool in s1 > s2 })
根據上下文推斷型別
如果Swift可以通過上下文推斷出引數和返回值型別,那麼返回箭頭和圍繞在引數的括號也可以被省略。
sortedStrings = Strings.sorted(by: { s1, s2 in return s1 > s2 })
通過內聯閉包表示式構造的閉包作為作為引數傳遞給函式或者方法的時候,總是可以推斷出它的型別。
單表示式閉包的隱式返回
因為只有一個簡單的單行表示式,我們可以省略return
直接返回結果:
sortedStrings = Strings.sorted(by: { s1, s2 in s1 > s2 })
函式引數名縮寫
我們可以通過$0
,$1
,$2
的方式按照順序呼叫閉包函式,這樣我們可以省略引數列表,對應縮寫會通過引數型別推斷,並且我們也省略了in
關鍵字:
sortedStrings = Strings.sorted(by: { $0 > $1 })
$0
,$1
代表第一個和第二個String
運算子方法
還有更簡短地方法編寫上面的式子。我們定義了>
來獲得一個布林值,因此我們可以直接只傳遞一個大於號,Swift會通過自動推斷找到對應的函式實現:
sortedStrings = Strings.sorted(by: >)
尾隨閉包
當我們需要把很長的閉包作為最後一個引數傳入函式,我們可以將閉包寫在括號外面。函式將其作為最後一個引數呼叫。使用尾隨閉包時,不需要再寫引數標籤。
sortedStrings = Strings.sorted(){ $0 > $1 }
如果閉包是函式的唯一引數,那麼括號可以省略:
sortedStrings = Strings.sorted { $0 > $1 }
值捕獲
閉包可以在其定義上下文中捕獲常量或者變數。即使這個量定義的原作用域已經不存在,閉包依然可以訪問和修改這些值。
Swift中最簡單的值捕獲是巢狀函式。巢狀函式可以捕獲外部函式的所有引數以及定義的常量和變數。如果巢狀函式使用了外部函式引數,那麼這個引數會被捕獲,即使沒有在函式內定義也可以使用。每次宣告一個新函式後,捕獲的引數都會有一個新引用,不同函式宣告之間互不干擾。
閉包是引用型別
我們是將閉包的引用賦給一個變數或者常量。即使是一個常量,我們還是可以通過呼叫函式改變內部的引數值。如果將閉包賦給兩個不同的變數和常量,它們會有同一個指向。
逃逸閉包
當一個閉包傳入函式後,這個閉包在函式返回時才執行,我們稱這個閉包逃逸。需要逃逸的閉包引數必須在引數型別前加上@escaping
。
一種需要逃逸的方法是把閉包儲存在外部變數中。這是一個非同步操作,這類函式會在非同步操作開始時立刻返回,但是閉包在非同步操作結束後才會呼叫。因此需要讓閉包逃逸。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
當我們在閉包中需要引用類的成員變數的時候,需要顯式的使用self
引用我們的變數,
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)
// 打印出“200”
completionHandlers.first?()
print(instance.x)
// 打印出“100”
自動閉包
自動閉包是自動建立的包,用於包裝傳遞給函式作為引數的表示式。這種閉包不接受任何引數,會直接返回表示式的值。有時候我們可以省略花括號,用一個表示式來代表閉包。自動閉包適用於() -> T
型別的函式。
自動閉包具有延遲求值的功能。他不會直接求值直到你呼叫這個閉包。
如果你希望直接引入這個閉包,並且不加上花括號使它看著像一個表示式並且有延遲求值的功能,那麼需要使用@autoclosure
屬性會自動將接收值轉換為閉包。如果希望一個自動閉包逃逸,那麼就需要使用@escaping
幫助逃逸。