閉包是自包含的函數代碼塊,能夠在代碼中被傳遞和使用。Swift 中的閉包與 C 和 Objective-C 中的代碼塊(blocks)以及其餘一些編程語言中的匿名函數比較類似。閉包能夠捕獲和存儲其所在上下文中任意常量和變量的引用。被稱爲包裹常量和變量。 Swift 會爲你管理在捕獲過程當中涉及到的全部內存操做。編程
閉包表達式語法有以下的通常形式:swift
{ (parameters) -> returnType in statements }
例如: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)和高計算成本的代碼來講是頗有益處的,由於它使得你能控制代碼的執行時機。