@autoclosure
在《函數(shù)式 Swift》第四章提到了 Swift 的 autoclosure 標簽能夠避免創(chuàng)建顯式閉包的需求。
在 Swift 中有一個特殊類型叫做函數(shù)類型,它由一個參數(shù)類型和返回值類型組成躲因,用于表示一個函數(shù)抵栈、方法或閉包的類型蕾管,形式如下
parameter type -> return type
autoclosure 標簽能夠?qū)⑻囟ū磉_式上的表達式當做隱式閉包來捕獲线衫,也就是將一個表達式當做函數(shù)類型來處理诲锹,例如對于下面一個函數(shù)
func testAutoClosure(target:@autoclosure () -> String) {
print(target())
}
它接受一個函數(shù)類型的參數(shù)繁仁,但是調(diào)用的時候可以直接傳入表達式作為參數(shù)
testAutoClosure(target: "Hello World")
假如沒有 autoclure 標簽,我們就需要按照如下幾種方式調(diào)用
testAutoClosure { () -> String in
return "Hello World"
}
testAutoClosure(target: {"Hello World"})
testAutoClosure{"Hello World"}
簡而言之辕狰,autoclosure 作用就是簡化閉包調(diào)用形式改备。
autoclosure 標簽在 Swift 系統(tǒng) API 中使用也很廣泛,例如 ?? 操作符的定義
func ??<T>(optional: T?, @autoclosure defaultValue: () -> T?) -> T?
func ??<T>(optional: T?, @autoclosure defaultValue: () -> T) -> T
這樣實現(xiàn)的原因是蔓倍,如果 ?? 第二個參數(shù)直接接受一個特定值悬钳,那么在調(diào)用時第二個參數(shù)如果是函數(shù)就必須先被執(zhí)行得到一個返回值,即使最終進行判斷后并沒有使用第二個參數(shù)偶翅,這樣會帶來不必要的計算消耗默勾,尤其是當?shù)诙€參數(shù)的函數(shù)內(nèi)進行的操作比較復雜時。那么定義一個閉包就可以在需要用到第二個參數(shù)時才執(zhí)行閉包內(nèi)的邏輯聚谁。進一步母剥,為了像上面一樣,能夠簡化閉包調(diào)用,所以使用 autoclosure 標簽修飾了閉包參數(shù)环疼。
當然使用 autoclosure 也有需要注意的地方
最后要提一句的是习霹,@autoclosure 并不支持帶有輸入?yún)?shù)的寫法,也就是說只有形如 () -> T 的參數(shù)才能使用這個特性進行簡化炫隶。另外因為調(diào)用者往往很容易忽視 @autoclosure 這個特性淋叶,所以在寫接受 @autoclosure 的方法時還請?zhí)貏e小心,如果在容易產(chǎn)生歧義或者誤解的時候伪阶,還是使用完整的閉包寫法會比較好煞檩。
@Escaping
escaping 是閉包的另一個修飾符。當閉包的某個參數(shù)在閉包返回后才被調(diào)用時稱這個參數(shù)是逃逸的參數(shù)栅贴,Swift 默認不允許閉包的參數(shù)逃逸斟湃,所以下面的定義是不能通過編譯的
func canNotEscape(target:()->Bool) -> ()->Bool{
return target
}
而下面這種形式就是可以逃逸的閉包參數(shù)
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
要注意一點,對于可以逃逸的閉包參數(shù)檐薯,其實現(xiàn)內(nèi)部必須顯式使用 self 引用凝赛,而非逃逸閉包參數(shù)則可以隱式使用 self 引用。
class SomeClass{
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
}