Swift學習(八):閉包(閉包表達式,尾隨閉包,閉包的定義,自動閉包)

閉包表達式(Closure Expression)

可以通過func定義一個函數,也可以通過閉包表達式定義一個函數

  • func函數

  • 閉包定義函數

閉包表達式調用可以直接省略參數名

閉包定義函數也可以寫爲:

  • 閉包表達式的結構


閉包表達式的簡寫


尾隨閉包

如果將一個很長的閉包表達式作爲函數的最後一個實參,使用尾隨閉包可以增強函數的可讀性 

尾隨閉包是一個被書寫在函數調用括號外面(後面)的閉包表達式

上面的函數使用閉包調用:

如果閉包表達式是函數的唯一實參,而且使用了尾隨閉包的語法,那就不需要在函數名後邊寫圓括號

針對上面的函數,一般與閉包調用的對比:


示例 - 數組的排序

數組排序的底層代碼的定義:

由上面的底層代碼可知,想要自定義一個數組排序,只需要傳入一個有兩個參數,返回值是Bool的函數

自定義排序方法的多種調用方式(閉包和簡寫):


閉包(Closure)

 網上有各種關於閉包的定義,個人覺得比較嚴謹的定義是 :

  • 一個函數和它所捕獲的變量\常量環境組合起來,稱爲閉包 
  • 一般指定義在函數內部的函數
  •  一般它捕獲的是外層函數的局部變量\常量

定義了一個閉包:

注:return plus前分配了一段堆空間,將num的值存儲到了這個堆空間,調用plus訪問的num實際上是堆空間的num, plus方法實際接收了兩個參數:i和堆空間地址

使用反彙編看它實現的底層彙編代碼:

由圖上紅色框標註的彙編代碼我們可以看出,在閉包裏調用了swift_allocObject方法,我們可以理解爲它在堆區申請了一塊內存空間,用來存儲它捕獲的外層函數的局部變量或常量也就是num,這也就決定了num不會立刻銷燬,由此我們可以看下它的計算結果:

func getFn() -> Fn {
    var num = 0
    func plus(_ i: Int) -> Int{
        num += 1
        return num
    }
    return plus
}

var fn = getFn()
print(fn(1))  //結果爲1
print(fn(2))  //結果爲3
print(fn(3))  //結果爲6
print(fn(4))  //結果爲10

可以看出計算結果是累加的,那就因爲num存儲在堆空間,沒有銷燬,上面的計算是對num的不斷累加。

注意:如果num是全局變量,則不會在堆空間開闢內存。

注意:每調用一次getFn()都會申請一個新的內存空間,舉例如下:

由上面的計算可以看出,fn1和fn2是分開獨立累加的,也就是fn1和fn2分別開闢了一塊新內存,互不影響

反彙編代碼如下:

由上面的反彙編代碼可以看出fn1和fn2前8個字節相同,因爲都是指向同一個plus方法,但是後8個字節不同,因爲分配的堆空間不同。

  •  可以把閉包想象成是一個類的實例對象
  •  內存在堆空間
  • 捕獲的局部變量\常量就是對象的成員(存儲屬性) 
  • 組成閉包的函數就是類內部定義的方法


閉包舉例

  • 第一個例子:

上面的閉包可以看成一個類:

  • 第二個例子:

上面的閉包同樣可以看成一個類:


參數注意

如果返回值類型是函數類型,那麼參數的修飾要保持統一


自動閉包

  •  @autoclosure 會自動將 20 封裝成閉包 { 20 }
  •  @autoclosure 只支持 () -> T 格式的參數
  • @autoclosure 並非只支持最後1個參數
  •  空合併運算符 ?? 使用了 @autoclosure 技術
  •  有@autoclosure、無@autoclosure,構成了函數重載

爲了避免與期望衝突,使用了@autoclosure的地方最好明確註釋清楚:這個值會被推遲執行。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章