Swift3.0 閉包(blcok)的全面介紹及使用

閉包是自包含的函數代碼塊,能夠在代碼中被傳遞和使用。Swift 中的閉包與 C 和 Objective-C 中的代碼塊(blocks)以及其餘一些編程語言中的匿名函數比較類似。閉包能夠捕獲和存儲其所在上下文中任意常量和變量的引用。被稱爲包裹常量和變量。 Swift 會爲你管理在捕獲過程當中涉及到的全部內存操做。編程

閉包表達式語法有以下的通常形式:swift

{
    (parameters) -> returnType in
      statements
 }
  • 這裏的參數(parameters),能夠是in-out(輸入輸出參數),但不能設定默認值。若是是可變參數,必須放在最後一位,否則編譯器報錯。元組也能夠做爲參數或者返回值。
  • 閉包的函數體部分由關鍵字in引入。該關鍵字表示閉包的參數和返回值類型定義已經完成,閉包函數體即將開始

例如:api

let  sum:(Int , Int) -> Int = {(a : Int ,b : Int) -> Int in
          return a + b  
}
 print(sum(3,4))

因爲能夠推斷出閉包的參數和返回值類型,因此閉包表達式能夠表達爲:閉包

let  sum:( Int , Int ) -> Int = { a , b  in
          return a + b 
 }

 

單表達式閉包隱式返回編程語言

單行表達式閉包能夠經過省略 return 關鍵字來隱式返回單行表達式的結果,如上版本的例子能夠改寫爲: ide

let  sum:( Int , Int ) -> Int = { a , b  in
          a + b 
 }

有參數沒有返回值的寫法函數

let say0:(String) ->Void = {
    (name: String) in
    print("hi \(name)")
}
say0("cdh")
//輸出結果:  hi cdh

沒有參數沒有返回值寫法優化

let say1:() ->Void = {
    () -> () in // 當沒有參數也沒有返回值是這行均可以省略
    print("hi cdh")
}
say1()
//輸出結果:  hi cdh

閉包的聲明spa

Swift用typealias聲明閉包數據類型,相似於Objective-C中的typedefcode

typealias  TestBlcok = (Int , Int) -> Int

let test : TestBlcok = {( a , b ) in  a + b}

let result = test( 12 , 20 )

計算結果是 32

 尾隨閉包

若將閉包做爲函數最後一個參數,能夠省略參數標籤,而後將閉包表達式寫在函數調用括號後面

func testFunction(testBlock: ()->Void){
    //這裏須要傳進來的閉包類型是無參數和無返回值的
    testBlock()
}
//正常寫法
testFunction(testBlock: {
    print("正常寫法")
})
//尾隨閉包寫法
testFunction(){
    print("尾隨閉包寫法")
}
//也能夠把括號去掉,也是尾隨閉包寫法。推薦寫法
testFunction { 
    print("去掉括號的尾隨閉包寫法")
}

 值捕獲

閉包能夠在其被定義的上下文中捕獲常量或變量。即便定義這些常量和變量的原做用域已經不存在,閉包仍然能夠在閉包函數體內引用和修改這些值。

Swift 中,能夠捕獲值的閉包的最簡單形式是嵌套函數,也就是定義在其餘函數的函數體內的函數。嵌套函數能夠捕獲其外部函數全部的參數以及定義的常量和變量。」

 例如:

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

makeIncrementor 返回類型爲 () -> Int。這意味着其返回的是一個函數,而非一個簡單類型的值。該函數在每次調用時不接受參數,只返回一個 Int 類型的值。

makeIncrementer(forIncrement:) 函數定義了一個初始值爲 0 的整型變量 runningTotal,用來存儲當前總計數值。該值爲 incrementor 的返回值。

makeIncrementer(forIncrement:) 有一個 Int 類型的參數,其外部參數名爲 forIncrement,內部參數名爲 amount,該參數表示每次 incrementor 被調用時 runningTotal 將要增長的量。makeIncrementer 函數還定義了一個嵌套函數 incrementor,用來執行實際的增長操做。該函數簡單地使 runningTotal 增長 amount,並將其返回。

若是咱們單獨考慮嵌套函數 incrementer(),會發現它有些不一樣尋常:

func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}

 incrementer() 函數並無任何參數,可是在函數體內訪問了 runningTotal 和 amount 變量。這是由於它從外圍函數捕獲了 runningTotal 和 amount 變量的引用。捕獲引用保證了 runningTotal 和 amount 變量在調用完 makeIncrementer 後不會消失,而且保證了在下一次執行 incrementer 函數時,runningTotal 依舊存在。

注意 :爲了優化,若是一個值不會被閉包改變,或者在閉包建立後不會改變,Swift 可能會改成捕獲並保存一份對值的拷貝。 Swift 也會負責被捕獲變量的全部內存管理工做,包括釋放再也不須要的變量。

閉包引發的循環強引用 

//定義一個全局變量:

        var a: Int = 9

        var closure: ()->Void = {

            /**

             默認閉包會對它訪問的對象執行強引用。
             這個地方訪問了self ,致使調用 該閉包的時候引用計數+1
             若是閉包屬性中沒有直接或者間接訪問self,就不會產生循環強引用。
             可使用unowned或者weak 來修飾self。
             固然兩種存在區別。Swift中的weak、unowned對應Objective-C中的weak、unsafe_unretained,
             這代表前者在對象釋放後 會自動將對象置爲nil,然後者依然保持一個「無效的」引用,若是此時調用這
             個「無效的」引用將會引發程序崩潰。對於後者的使用,咱們必須保證訪問的時候對象沒有釋放。
             相反,weak則友好一點咱們也更加習慣使用它,

             */

       [weak self] in

       print("a=\(self.a)")

}

逃逸閉包

當一個閉包做爲參數傳到一個函數中,可是這個閉包在函數返回以後才被執行,咱們稱該閉包從函數中逃逸。當你定義接受閉包做爲參數的函數時,你能夠在參數名以前標註 @escaping,用來指明這個閉包是容許「逃逸」出這個函數的。

自動閉包

自動閉包是一種自動建立的閉包,用於包裝傳遞給函數做爲參數的表達式。這種閉包不接受任何參數,當它被調用的時候,會返回被包裝在其中的表達式的值。這種便利語法讓你可以省略閉包的花括號,用一個普通的表達式來代替顯式的閉包。

自動閉包讓你可以延遲求值,由於直到你調用這個閉包,代碼段纔會被執行。延遲求值對於那些有反作用(Side Effect)和高計算成本的代碼來講是頗有益處的,由於它使得你能控制代碼的執行時機。

相關文章
相關標籤/搜索