位置:首頁 > 高級語言 > Swift教學 > Swift尾隨閉包

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]

如上代碼創建了一個數字位和它們名字映射的英文版本字典。 同時定義了一個準備轉換為字符串的整型數組。

您現在可以通過傳遞一個尾隨閉包給numbersmap方法來創建對應的字符串版本數組。 需要注意的時調用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函數的括號內。