閉包
Swift對閉包進行了簡化:
- 利用上下文推斷參數和返回值類型
- 隱式返回單表達式閉包,即單表達式閉包可以省略
return關鍵字 - 參數名稱縮寫
- 尾隨(Trailing)閉包語法
先來看一個排序的例子,數組的降序排列
var usernames = ["Lves", "Wildcat", "Cc", "Lecoding"]
func backWards(s1: String, s2: String) -> Bool
{
return s1 > s2
}
var resultName1 = usernames.sort(backWards)
//resultName1: ["Wildcat", "Lves", "Lecoding", "Cc"]
1. 閉包表達式語法
swift中閉包的一版形式如下:
{ (parameters) -> returnType in
statements
}
下面我們用內聯閉包實現數組的升序排列,
函數的最后一個參數是閉包時 () 可以省略;
注意:此時閉包的參數和返回值類型要和函數聲明相同,(Self.Generator.Element, Self.Generator.Element) -> Bool
var resultName2 = usernames.sort { (s1: String, s2: String) -> Bool in
return s1 < s2
}
//resultName2 : ["Cc", "Lecoding", "Lves", "Wildcat"]
1.2 根據上下文推斷類型(Inferring Type From Context)
上面的內聯閉包書寫還可以更加精簡,因為sort(_:)函數需要的函數參數類型已知即:(String, String) -> Bool,
因為所有的類型都可以被正確推斷,返回箭頭(->)和圍繞在參數周圍的括號也可以被省略:
var resultName3 = usernames.sort { s1, s2 in
return s1 > s2
}
//["Wildcat", "Lves", "Lecoding", "Cc"]
1.3 單表達式閉包隱式返回
單行表達式閉包可以通過省略return關鍵字來隱式返回單行表達式的結果
var resultName4 = usernames.sort { s1, s2 in s1 > s2 }
1.4 參數名稱縮寫
如果你覺得還不夠精簡,當然還有更加精簡的:
內聯閉包可以省略參數名直接用參數順序$0,$1,$2調用.
var resultName5 = usernames.sort ({ $0 > $1 })
1.5 運算符函數(Operator Functions)
對於上邊的排序函數,你覺得內聯閉包書寫是最精簡了,那你就錯了。
Swift的String類型定義了關於大於號(>)的字符串實現,其作為一個函數接受兩個String類型的參數並返回Bool類型的值。而這正好與sort(_:)方法的第二個參數需要的函數類型相符合。
var resultName6 = usernames.sort(>)
2. 尾隨閉包(Trailing Closures))
尾隨閉包是一個書寫在函數括號之后的閉包表達式,函數支持將其作為最后一個參數調用:
下面先定義一個計算函數,參數為:兩個整數和一個函數類型參數
func caculateTwoNumbers(num1: Int, num2: Int, CaluFunction: (Int, Int) -> Int) -> Int{
return CaluFunction(num1, num2)
}
//內聯閉包形式,不使用尾隨閉包,求和
var numReult1 = caculateTwoNumbers(2, num2: 3,CaluFunction: {(num1: Int, num2: Int) -> Int in
return num1 + num2
})
//5
//使用尾隨閉包,求乘機
var numReult2 = caculateTwoNumbers(3, num2: 4) { $0 * $1 }
numReult2 //7
3. 捕獲值(Capturing Values)
閉包可以在其被定義的上下文中捕獲常量或變量。即使定義這些常量和變量的原作用域已經不存在,閉包仍然可以在閉包函數體內引用和修改這些值。
4.閉包是引用類型(Closures Are Reference Types)
和類一樣,必要也是引用類型
5. 非逃逸閉包(Nonescaping Closures)
一個閉包作為參數傳到一個函數中,但是這個閉包在函數返回之后才被執行,我們稱該閉包從函數中逃逸。可以在參數名之前標注@noescape,用來指明這個閉包是不允許“逃逸”出這個函數的。將閉包標注@noescape能使編譯器知道這個閉包的生命周期.
像剛才的數組的sort(_:)函數中的參數就定義成了非逃逸閉包,
public func sort(@noescape isOrderedBefore: (Self.Generator.Element, Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
你可能會問什么時候會出現逃逸閉包呢?舉個例子:很多啟動異步操作的函數接受一個閉包參數作為 completion handler。這類函數會在異步操作開始之后立刻返回,但是閉包直到異步操作結束后才會被調用。在這種情況下,閉包需要“逃逸”出函數,因為閉包需要在函數返回之后被調用。
非逃逸閉包和逃逸閉包講的不是執行先后順序吧,非逃逸是指你的閉包不能在函數外單獨調用,只能在函數內部調用,函數調用完成后,那個閉包也就結束了。
下面舉個逃逸閉包的例子:
//聲明一個存放函數的數組
var functionArray: [() -> Void] = []
//定義一個接收閉包參數的函數,如果定義非逃逸函數 func doSomething(@noescape paramClosure:() -> Void) 就會編譯錯誤
func doSomething(paramClosure:() -> Void){
//把參數放入數組中,用於逃逸調用
functionArray.append(paramClosure)
}
//調用函數
doSomething({print("Hello world")})
doSomething({print("Hello LvesLi")})
//逃逸調用閉包
for closurePrama in functionArray {
print("\(closurePrama)")
}
