Swift尾隨閉包
尾隨閉包(Trailing Closures)
如果您需要將一個很長的閉包表達式作爲最後一個參數傳遞給函數,可以使用尾隨閉包來增強函數的可讀性。 尾隨閉包是一個書寫在函數括號之後的閉包表達式,函數支持將其作爲最後一個參數調用。
func someFunctionThatTakesAClosure(closure: () -> ()) {
// 函數體部分
}
// 以下是不使用尾隨閉包進行函數調用
someFunctionThatTakesAClosure({
// 閉包主體部分
})
// 以下是使用尾隨閉包進行函數調用
someFunctionThatTakesAClosure() {
// 閉包主體部分
}
注意:
如果函數只需要閉包表達式一個參數,當您使用尾隨閉包時,您甚至可以把()
省略掉。
在上例中作爲sort
函數參數的字符串排序閉包可以改寫爲:
reversed = sort(names) { $0 > $1 }
當閉包非常長以至於不能在一行中進行書寫時,尾隨閉包變得非常有用。 舉例來說,Swift 的Array
類型有一個map
方法,其獲取一個閉包表達式作爲其唯一參數。 數組中的每一個元素調用一次該閉包函數,並返回該元素所映射的值(也可以是不同類型的值)。 具體的映射方式和返回值類型由閉包來指定。
當提供給數組閉包函數後,map
方法將返回一個新的數組,數組中包含了與原數組一一對應的映射後的值。
下例介紹瞭如何在map
方法中使用尾隨閉包將Int
類型數組[16,58,510]
轉換爲包含對應String
類型的數組["OneSix", "FiveEight", "FiveOneZero"]
:
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
如上代碼創建了一個數字位和它們名字映射的英文版本字典。 同時定義了一個準備轉換爲字符串的整型數組。
您現在可以通過傳遞一個尾隨閉包給numbers
的map
方法來創建對應的字符串版本數組。 需要注意的時調用numbers.map
不需要在map
後面包含任何括號,因爲其只需要傳遞閉包表達式這一個參數,並且該閉包表達式參數通過尾隨方式進行撰寫:
let strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
// strings 常量被推斷爲字符串類型數組,即 String[]
// 其值爲 ["OneSix", "FiveEight", "FiveOneZero"]
map
在數組中爲每一個元素調用了閉包表達式。 您不需要指定閉包的輸入參數number
的類型,因爲可以通過要映射的數組類型進行推斷。
閉包number
參數被聲明爲一個變量參數(變量的具體描述請參看常量參數和變量參數),因此可以在閉包函數體內對其進行修改。閉包表達式制定了返回類型爲String
,以表明存儲映射值的新數組類型爲String
。
閉包表達式在每次被調用的時候創建了一個字符串並返回。 其使用求餘運算符 (number % 10) 計算最後一位數字並利用digitNames
字典獲取所映射的字符串。
注意:
字典digitNames
下標後跟着一個歎號 (!),因爲字典下標返回一個可選值 (optional value),表明即使該 key 不存在也不會查找失敗。
在上例中,它保證了number % 10
可以總是作爲一個digitNames
字典的有效下標 key。
因此歎號可以用於強制解析 (force-unwrap) 存儲在可選下標項中的String
類型值。
從digitNames
字典中獲取的字符串被添加到輸出的前部,逆序建立了一個字符串版本的數字。 (在表達式number % 10
中,如果number爲16,則返回6,58返回8,510返回0)。
number
變量之後除以10。 因爲其是整數,在計算過程中未除盡部分被忽略。 因此 16變成了1,58變成了5,510變成了51。
整個過程重複進行,直到number /= 10
爲0,這時閉包會將字符串輸出,而map
函數則會將字符串添加到所映射的數組中。
上例中尾隨閉包語法在函數後整潔封裝了具體的閉包功能,而不再需要將整個閉包包裹在map
函數的括號內。