Closure,你可以聽到有人稱它為閉包,官方文件上是這樣解釋它:
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
Closure 是一個獨立的程式碼區塊,可以被用來傳遞及使用,這句話是不是很耳熟,就很像前幾篇介紹的 Function 不是嗎?
確實他們很相似,但還是有些差別,讓我們來看看到底什是 Closure。
我們先來看看一個簡單的 Function:
func add(value1: Int, value2: Int) -> Int {
return value1 + value2
}
print(add(value1: 5, value2: 3)) // 8
Closure 的語法:
{(參數列) -> 回傳值型別 in
...
}
所以用 Closure 表達會像是這樣:
let add = {(value1: Int, value2: Int) -> Int in
return value1 + value2
}
print(add(5, 3)) // 8
Closure 有個特性就是,他雖然是一個獨立的程式碼區塊,但又不像 Functions 這麼獨立,因為 Closure 並沒辦法真正獨立存在,他必須指派給一個常數或是變數,或是作為 Function 的參數,像上面這個範例一樣,將一個 Closure 指派給 add 常數。
還記得之前在介紹 Functions 時,無回傳值 Functions 嗎?這類的 Functions 又可被轉換為 Closure。
func 說你好() {
print("你好")
}
說你好() // "你好"
let 說你好 = { () -> () in
print("你好")
}
說你好()
因為 Closure 無法單獨存在,所以必須指派給一個常數或變數。
還記得這種無回傳值的 Function 是一個 Void 類型的 Function 嗎?他其實是會回傳 () -> ()
,所以在 Closure 表達上他其實是可以被寫成這樣,但是其實還可以再更精簡:
let 說你好 = {
print("你好")
}
說你好()
對於這種匿名 Functions,除了原本可以省略 return 的單行表達式,() -> ()
也是可以被省略的喔,像這種 Closure 表達是不是看起來更簡潔一點了呢?
可以將 Closure 當作參數,傳入 Functions,來看下面這個範例:
func 說你好(action: () -> ()) {
action()
}
說你好(action: {
print("你好")
})
在 說你好()
的參數輸入,有一個 action 的參數,他的型態就是一個 Closure,然後這個 Function 中又會在呼叫這個 action()
,所以我們在呼叫的時候,就可以針對這個 action()
去做特定的事情,但是這可以再被簡化:
func 說你好(action: () -> ()) {
action()
}
說你好 {
print("你好") // 你好
}
當這個 Closure
為最後一個參數時,我們就可以將這個參數標籤省略掉,直接使用 {}
,這種方式叫做 Trailing Closures,我們來看另外一個多個參數傳入的例子:
func 說你好(次數: Int, action: () -> ()) {
for _ in 1...次數 {
action()
}
}
說你好(次數: 5) {
print("你好")
}
/*
Prints:
你好
你好
你好
你好
你好
*/
這也是一個 Trailing Closures 的範例,Closure 一樣位於最後一個參數,所以 Closure 的參數標籤可以被省略,但是在這個 Closure 參數之前的參數都必須得像使用 Functions 照常輸入。
Functions 允許輸入多個參數,Closure 也不例外,我們來看一下下面的範例:
func 打招呼(姓名: String, 內容: (String, String) -> ()) {
內容("很開心看見你!", "你好啊!\(姓名)")
}
打招呼(姓名: "安竹", 內容: { (話語1: String, 話語2: String) -> () in
print(話語1 + 話語2) // 很開心看見你!你好啊!安竹
})
在 內容
這個參數被指派為一個 Closure,並且有兩個 String 的參數,所以在調用上 {}
,可以宣告兩個常數來承接這兩個 String,所以上面這個例子可以看到,Function 中再去呼叫內容
,並傳遞兩個字串的參數,所以在呼叫打招呼()
這個方法時,我建立了兩個常數話語1
和話語2
,來承接,然而就可以在 Closure 中來使用這兩個常數。
當然,因為這也是 Trailing Closures,所以可以在簡化一點:
打招呼(姓名: "安竹") { (話語1: String, 話語2: String) -> () in
print(話語1 + 話語2)
}
你以為結束了嗎?還可以在簡化一點!
打招呼(姓名: "安竹") { (話語1, 話語2) -> () in
print(話語1 + 話語2)
}
我們可以省略掉在 Closure 中兩個參數的型別註記,這是貼心的 Swift 幫我們自動推斷的,Swift 從被呼叫 Function 中來去推斷 Closure 參數的型別或是回傳值的型別,這種方式叫做 Inferring Type From Context。
然而 Closure 中宣告的參數被寫在 ()
中,這個其實也是可以被省略的,回傳值的註記也是,所以又可以被簡化成這樣:
打招呼(姓名: "安竹") { 話語1, 話語2 in
print(話語1 + 話語2)
}
但這也不是最省略的方式,在 Closure 中,你可使用 $0
和 $1
用來代表 Closure 的第一個參數以及第二個參數,當然也可能會有 $2
,就依照建立 Function 時的 Closure 參數數量,所以可以用 $0
以及 $1
來省略在 Closure 中宣告常數,所以最後可以被最簡化成這樣:
打招呼(姓名: "安竹") {
print($0 + $1)
}