尾隨閉包(Trailing Closures)
如果你需要將一個很長的閉包表達式作為最后一個參數傳遞給函數,可以使用尾隨閉包來增強函數的可讀性,尾隨閉包是一個書寫在函數括號之后的閉包表達式,函數支持將其作為最后一個參數調用:
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]
let strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
閉包語法:
{ (parameters) -> returnType in
statements
}
map為數組中每一個元素調用了閉包表達式,不需要指定閉包輸入參數的類型,因為可以通過要映射的數組類型進行推斷。
在該例子中,閉包number參數被聲明為一個變量參數,因此可以在閉包函數體內對其進行修改,不用再定義一個局部變量并將number的值賦值給它,閉包表達式指定了返回類型為String,以表明存儲映射的新數組的類型是String,閉包表達式在每次被調用的時候創建了一個叫做output的字符串并且返回,使用求余(number % 10)計算最后一位數字并且利用digitNames字典獲取所映射的字符串,然后再除以10賦值給number.
比如16對10取余數是6,就獲取到了16的最后一位,然后16除以10賦值給number,number就是1,繼續循環,1對10取余數是1(商是0,余數是1),這時候number在除以10的時候,得到的就是0,下次循環的時候就不會繼續了。這個16輸入的就是OneSix。
總結:通過這個例子,我理解的閉包就是另外一個函數,我們通過封裝后,我們將數組的每個元素作為輸入,輸入給閉包函數,閉包進行轉換,輸出String。
捕獲值(Capturing Values)
閉包可以在其被定義的上下文中捕獲常量或者變量,及時定義了這些常量或者變量的作用域已經不存在,閉包仍然可以在閉包函數體內引用和修改這些值。
swift中可以捕獲值的閉包的最簡單的形式是嵌套函數,也就是定義在其他函數的函數體內的函數,嵌套函數可以捕獲其外部函數所有的參數以及定義的常量和變量。
func makeIncrementor(forIncrement amount: Int) -> () ->Int{
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
我們定義了一個嵌套函數makeIncrementor中嵌套了一個函數叫做incrementor,makeIncrementor這個函數返回的值是() ->Int,返回的是一個函數,不是一個值,返回的這個函數就是 incrementor,可以看見這個內嵌的函數沒有輸入值,輸出的值是Int類型的值,makeIncrementor入參是(forIncrement amount: Int) ,外部參數名字是forIncrement,內部的參數名是amount。
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
可以看見這個函數并沒有任何的參數,單數函數體的內部訪問了runningTotal和amount,這兩個值是從外圍函數捕獲的runningTotal和amount變量的引用,捕獲引用保證了runningTotal和amount在調用完makeIncrementor后不會消失,并保證下次執行incrementor,runningTotal依舊存在。
let incrementByTen = makeIncrementor(forIncrement: 10)
incrementByTen()
// 返回的值為10
incrementByTen()
// 返回的值為20
incrementByTen()
// 返回的值為30
如果我們創建另一個incrementor,它擁有自己一個全新、獨立的runningTotal變量的引用,對incrementByTen的runningTotal沒有影響。
閉包是引用類型(Closures Are Reference Types)
func makeIncrementor(forIncrement amount: Int) -> () ->Int{
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
上面的例子incrementBySeven和incrementByTen是常量,但是這些常量指向的閉包仍然是可以增加其捕獲的變量的值,這是因為函數和閉包是引用類型。
無論您是講函數或者閉包賦值給一個常量或者變量,您實際上是將常量或者變量的值設定為對應的函數或者閉包的引用,上面的例子中,指向閉包的引用incrementByTen是一個常量,而非閉包內容本身。
這也就意味著,如果我們將閉包同時賦值給兩個不同的常量或者變量,兩個值都會指向同一個閉包:
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
我們定義了一個變量,alsoIncrementByTen都是引用的同一個閉包,這個變量變化的話,我們定義的變量都會相應的變化。
非逃逸閉包(Nonescaping Closures)
當一個閉包作為參數傳入到另一個函數中,但是這個閉包在函數返回之后才被執行,我們稱該閉包從函數中逃逸,當你定義接收閉包作為參數的函數時,你可以在參數名前面標注@noescape,用來指明這個閉包是不允許“逃逸”出這個函數的。