一、簡介
閉包(Closures)是自包含的功能代碼塊,可以在代碼中使用或者用來作為參數(shù)傳值。
Swift 中的閉包與 C 和 OC 中的代碼塊(blocks)以及其他一些編程語言中的 匿名函數(shù) 比較相似。全局函數(shù)和嵌套函數(shù)其實就是特殊的閉包。
由于之前對 Swift 中的閉包不太熟悉,所以在此歸納總結(jié)一下閉包的語法。
二、語法
Swift 中的閉包有很多優(yōu)化的地方:
- 根據(jù)上下文推斷參數(shù)和返回值類型
- 從單行表達式閉包中隱式返回(也就是閉包體只有一行代碼,可以省略 return)
- 可以使用簡化參數(shù)名,如$0, $1(從 0 開始,表示第 i 個參數(shù)...)
- 提供了尾隨閉包語法(Trailing closure syntax)
閉包表達式 提供了一些語法優(yōu)化,使得撰寫閉包變得簡單明了。下面 閉包表達式 的例子通過使用幾次迭代展示了 sorted(by:) 方法定義和語法優(yōu)化的方式。下面的每一次迭代都用更簡潔的方式描述了相同的功能。??->??->??->??->??->??->??。
- sorted(by:) 函數(shù)介紹:
Swift 標(biāo)準(zhǔn)庫提供了名為sorted(by:)的方法,它會根據(jù)你所提供的用于排序的閉包函數(shù)將已知類型數(shù)組中的值進行排序。一旦排序完成,sorted(by:)方法會返回一個與原數(shù)組大小相同,包含同類型元素且元素已正確排序的新數(shù)組。原數(shù)組不會被sorted(by:)方法修改。
下面的閉包表達式示例使用 sorted(by:)方法對一個 String 類型的數(shù)組進行字母逆序排序。以下是初始數(shù)組:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
sorted(by:)方法接受一個 閉包,該閉包函數(shù)需要傳入與數(shù)組元素類型相同的兩個值,并返回一個布爾類型值來表明當(dāng)排序結(jié)束后傳入的第一個參數(shù)排在第二個參數(shù)前面還是后面。如果第一個參數(shù)值出現(xiàn)在第二個參數(shù)值前面,排序閉包函數(shù)需要返回 true,反之返回 false。
該例子對一個 String 類型的數(shù)組進行排序,因此排序閉包函數(shù)類型需為 (String, String) -> Bool。
??原始實現(xiàn)方式:
提供排序閉包函數(shù)的一種方式是撰寫一個符合其類型要求的普通函數(shù),并將其作為 sorted(by:)方法的參數(shù)傳入:
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// 打印可得 reversedNames 為 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
然而,以這種方式來編寫一個實際上很簡單的表達式(a > b),確實太過繁瑣了。對于這個例子來說,利用 閉包表達式語法 可以更好地構(gòu)造一個 內(nèi)聯(lián)排序閉包。
閉包表達式語法:
閉包表達式語法有如下的一般形式:
{ (parameters) -> return type in
statements
}
閉包表達式參數(shù) 可以是 in-out 參數(shù),但不能設(shè)定默認(rèn)值。如果你命名了可變參數(shù),也可以使用此可變參數(shù)。元組也可以作為參數(shù)和返回值。
??第一次精簡——閉包語法:
下面的例子展示了之前 backward(_:_:) 函數(shù)對應(yīng)的閉包表達式版本的代碼:
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
需要注意的是內(nèi)聯(lián)閉包參數(shù)和返回值類型聲明與 backward(_:_:) 函數(shù)類型聲明相同。在這兩種方式中,都寫成了 (s1: String, s2: String) -> Bool。然而在內(nèi)聯(lián)閉包表達式中,函數(shù)和返回值類型都寫在大括號 內(nèi),而不是大括號 外。
- 關(guān)鍵字 in:閉包的函數(shù)體部分由關(guān)鍵字 in 引入。該關(guān)鍵字表示 “閉包的參數(shù)和返回值類型定義已經(jīng)完成,閉包函數(shù)體即將開始”。
該例中 sorted(by:) 方法的整體調(diào)用保持不變,一對圓括號仍然包裹住了方法的整個參數(shù)。然而,參數(shù)現(xiàn)在變成了 內(nèi)聯(lián)閉包。
??第二次精簡——根據(jù)上下文推斷類型:
因為排序閉包函數(shù)是作為 sorted(by:) 方法的參數(shù)傳入的,Swift 可以推斷其參數(shù)和返回值的類型。sorted(by:) 方法被一個字符串?dāng)?shù)組調(diào)用,因此其參數(shù)必須是 (String, String) -> Bool 類型的函數(shù)。這意味著 (String, String) 和 Bool 類型并不需要作為閉包表達式定義的一部分。因為所有的類型都可以被正確推斷,返回箭頭(->)和圍繞在參數(shù)周圍的括號也可以被省略:
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
實際上,通過內(nèi)聯(lián)閉包表達式構(gòu)造的閉包作為參數(shù)傳遞給函數(shù)或方法時,總是能夠推斷出閉包的參數(shù)和返回值類型。這意味著閉包作為函數(shù)或者方法的參數(shù)時,你幾乎不需要利用完整格式構(gòu)造內(nèi)聯(lián)閉包。
??第三次精簡——單表達式閉包隱式返回:
單行表達式閉包可以通過省略 return 關(guān)鍵字來隱式返回單行表達式的結(jié)果,如上版本的例子可以改寫為:
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
在這個例子中,sorted(by:) 方法的參數(shù)類型明確了閉包必須返回一個 Bool 類型值。因為閉包函數(shù)體只包含了一個單一表達式(s1 > s2),該表達式返回 Bool 類型值,因此這里沒有歧義,return 關(guān)鍵字可以省略。
??第四次精簡——參數(shù)名稱縮寫:
Swift 自動為內(nèi)聯(lián)閉包提供了參數(shù)名稱縮寫功能,你可以直接通過 $0,$1,$2 來順序調(diào)用閉包的參數(shù),以此類推。
如果你在閉包表達式中使用參數(shù)名稱縮寫,你可以在閉包定義中省略參數(shù)列表,并且對應(yīng)參數(shù)名稱縮寫的類型會通過函數(shù)類型進行推斷。in 關(guān)鍵字也同樣可以被省略,因為此時閉包表達式完全由閉包函數(shù)體構(gòu)成:
reversedNames = names.sorted(by: { $0 > $1 } )
在這個例子中,$0 和 $1 表示閉包中第一個和第二個 String 類型的參數(shù)。
??第五次精簡——運算符方法:
實際上還有一種更簡短的方式來編寫上面例子中的閉包表達式。Swift 的 String 類型定義了關(guān)于大于號(>)的字符串實現(xiàn),其作為一個函數(shù)接受兩個 String 類型的參數(shù)并返回 Bool 類型的值。而這正好與 sorted(by:) 方法的參數(shù)需要的函數(shù)類型相符合。因此,你可以簡單地傳遞一個大于號,Swift 可以自動推斷出你想使用大于號的字符串函數(shù)實現(xiàn):
reversedNames = names.sorted(by: >)
??第六次精簡——尾隨閉包:
- 什么是尾隨閉包?
如果你需要將一個很長的閉包表達式作為最后一個參數(shù)傳遞給函數(shù),可以使用 尾隨閉包 來增強函數(shù)的可讀性。尾隨閉包是一個書寫在函數(shù)括號之后的閉包表達式,函數(shù)支持將其作為最后一個參數(shù)調(diào)用。在使用尾隨閉包時,你不用寫出它的參數(shù)標(biāo)簽:func someFunctionThatTakesAClosure(closure: () -> Void) { // 函數(shù)體部分 } // 以下是不使用尾隨閉包進行函數(shù)調(diào)用 someFunctionThatTakesAClosure(closure: { // 閉包主體部分 }) // 以下是使用尾隨閉包進行函數(shù)調(diào)用 someFunctionThatTakesAClosure() { // 閉包主體部分 }
在上面 sorted(by:) 方法參數(shù)的字符串排序閉包可以改寫為:
reversedNames = names.sorted() { $0 > $1 }
如果閉包表達式是函數(shù)或方法的唯一參數(shù),則當(dāng)使用尾隨閉包時,甚至可以把 () 省略掉:
reversedNames = names.sorted { $0 > $1 }
以上只是列舉了閉包的一些基本語法與用法,還有一些其他概念需要繼續(xù)學(xué)習(xí),如 自動閉包、逃逸閉包,后續(xù)我會慢慢補齊總結(jié)的,謝謝!
以上的總結(jié)參考了并部分摘抄了以下文章,非常感謝以下作者的分享?。?br> 《The Swift Programming Language 中文版》
轉(zhuǎn)載請備注原文出處,不得用于商業(yè)傳播——凡幾多