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)

對於上邊的排序函數,你覺得內聯閉包書寫是最精簡了,那你就錯了。

SwiftString類型定義了關於大於號(>)的字符串實現,其作為一個函數接受兩個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)")

}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM