閉包
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)") }