Swift基礎之閉包

jopen 8年前發布 | 8K 次閱讀 閉包 Swift Apple Swift開發

閉包

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) -&gt; Bool) -&gt; [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)")

}

微信公眾賬號: lecoding 同步更新,你也可以掃描下方二維碼關注我們。

來自: http://www.lvesli.com/?p=402

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!