Swift-閉包
Swift閉包的含義
閉包是自包含的功能代碼塊,可以用作函數的參數或者返回值
閉包可以捕獲上下文中的任何常量以及變量的引用-變量和常量的自封閉
閉包是引用類型
Swift中閉包的表現形式
全局函數:全局函數都是閉包,這些閉包都有命名,但是不能捕獲任何值
嵌套函數:嵌套函數也都是閉包,這些閉包有名字,并且能夠捕獲當前上下文中的常量和變量的值
閉包表達式:閉包表達式都是無名閉包,可以捕獲上下文中的值
簡單了解一下閉包
-
最簡單的閉包:
{}
閉包的執行:
{}()
OK,玩一下最接地氣的playground
定義閉包:
閉包執行:
- 項目中經常使用到的閉包
-
懶加載
private lazy var tmpStr:UILabel = { let tmpLabel:UILabel = UILabel() return tmpLabel }()
-
定義網絡回調(類似OC網絡回調的Block)
typealias NETERRORCLOCURE = (ErrorProtocol) -> Void typealias NETSUCCESSCLOSURE = (AnyObject) -> Void import UIKit class ZHNetWorkApi: NSObject { static func login(phoneNum:String, password:String, netError error:NETERRORCLOCURE? = nil, success successData:NETSUCCESSCLOSURE? = nil){ } }
調用接口:
ZHNetWorkApi.login(phoneNum: "zhu", password: "hou", netError: {[weak self] (error) in guard let instance = self else { return } instance.tmpStr.text = "lin" }) {[weak self] (data) in }
-
官方文檔中的閉包
閉包-Closures
Sorted方法-The Sorted Method
閉包表達式-Closure Expression Syntax
類型推斷-Inferring Type From Context
-
省略return的單行表達式閉包-Implicit Returns from Single-Expression Closures
- 官方描述:“Single-expression closures can implicitly return the result of their single expression by omitting the return keyword from their declaration” 單一表達式閉包在返回結果的時候可以省略return關鍵字 - 例子: 我們首先聲明一個方法,該方法兩個參數,一個NSinteger類型的數據,一個閉包,該閉包有參數有返回值: func closureTest_A(a:NSInteger,closure:((NSInteger, NSInteger) -> NSInteger)) { closure(a, a - 10) } 我們來調用一下這個函數: - 調用方式一: closureTest_A(a: 10) { (aa, bb) -> NSInteger in return aa + bb } 注意:這是一個尾隨閉包(下面有介紹),所以閉包可以寫在()之外 - 調用方式二: closureTest_A(a: 10) { (aa, bb) -> NSInteger in aa + bb } - 調用方式三: 我可能需要在閉包中做更多的計算操作,然后才返回數據 closureTest_A(a: 10) { (aa, bb) -> NSInteger in let cc = aa - bb return aa + bb } OK,那么問題來了,怎么是不正確的調用呢?很簡單,將上面的return省略掉,這時候就會報錯“missing return ...”。也就是說,當閉包中含有多個表達式的時候,是不可省略return關鍵字的(除非閉包沒有返回值)。原因是這種情況下,swift無法確定那個表達式才是真正應該返回的數據 那么有的同學就會想了,一個返回Bool類型的閉包中,去執行兩個表達式語句,一個是bool類型,一個是NSinteger類型,這種情況下省略return可以自動返回Bool類型的運算結果嗎?答案是不會的,還是同樣的報錯“missing return”,下面是錯誤的例子 closureTest_A(a: 10) { (aa, bb) -> NSInteger in let cc = aa > bb aa + bb } - 總結: 閉包省略renturn的充要條件: - 閉包有返回值 - 閉包有且只有一條執行語句 - 該條執行語句的結果類型和閉包的返回值類型必須一致
-
參數名簡寫-Shorthand Argument Names
- 官方描述:“Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.” 意思很簡單,就是說,訪問內聯閉包的參數的時候可以用‘$’+下標(第幾個參數)的形式。$0就是訪問第一個參數,$1就是訪問第二個參數。。。 - 官方例子: let reversedNames = names.sorted(isOrderedBefore: { $0 > $1 }) 再看一下這個函數的聲明 public func sorted(isOrderedBefore: @noescape (Element, Element) -> Bool) -> [Element] 上面的函數的參數是一個非逃逸閉包,這個非逃逸閉包有兩個參數,對這兩個參數的訪問,可以使用'$' + 下標的方式 而一般我們比較習慣的寫法是下面這樣的: let bbb = names.sorted {[unowned self] (a, b) -> Bool in return a > b } OK,我們來看看這個方法是怎么一步步演化的 首先是最繁瑣的調用-參數名+參數類型(閉包-參數+返回值) let reversedNames = names.sorted(isOrderedBefore: { (a,b) -> Bool in return a > b }) OK我們再簡單一點點,怎么簡單呢?哈哈,他是一個尾隨閉包,再次簡化 let reversedNames = names.sorted { (a, b) -> Bool in return a > b } 再來,通過下標訪問簡化操作 let bbb = names.sorted { return $0 > $1 } 再來,把return也干掉 let bbb = names.sorted { $0 > $1 } - 注意事項,如果閉包的的參數是明確的,也就是 ‘in’和之前的代碼沒有省略的情況下,是不可以使用'$' + 下標的方式訪問閉包參數的,否則會報如下錯誤“ Anonymous closure arguments cannot be used inside a closure that has explicit arguments” - 讓我們來看一個有趣的現象:數組的排序 我們在上面使用過了一個方法 names.sorted,如果你自己試驗的時候回發現一個很有其的現象
-
運算符函數-Operator Functions
- 官方描述:
“Classes and structures can provide their own implementations of existing operators. This is known as overloading the existing operators.”
也就是說,類和結構體是能夠針對已存在的運算符(‘+’,’-‘,’*‘,’、‘)實現自己的對于這些運算符的操作。這種方式叫做重載運算符。
實際上,運算符不單單可以被重載,如果有需要的話,你也可以定義自己的運算符,比如'+++','---','==='。- 官方例子: reversedNames = names.sorted(isOrderedBefore: >) 上面的例子,是官方文檔中給出的例子,可以看到參數是一個'>',按住Commond點擊去之后可以查看'>'的定義: public func ><T : Comparable>(lhs: T, rhs: T) -> Bool 很明顯這是一個閉包,有兩個參數和一個返回值
-
尾隨閉包-Trailing Closures
- 官方對尾隨閉包的描述
“A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function”
簡單來說尾隨閉包就是當一個函數或者方法有一個閉包作為參數回調,那么閉包可以寫在"()"的后面,而不是寫在"()"之間
- 舉例子描述-01func funcA(str:NSString, closure:() -> Void) { _ = 1 } 上面定義了一個帶有閉包參數的函數,我們看一些對這個函數的調用方式: - 非尾隨閉包方式調用 funcA(str: "zhuhoulin", closure: { }) 閉包包含在"()"之內 - 尾隨閉包的方式調用 funcA(str: "zhuhoulin") { } 閉包不包含在"()"之內 - 舉例子描述-02 func funcB(str:NSString, closure_A:() -> Void, closure_B:() -> Void) { } 上面的例子中有兩個閉包參數,同樣也會有兩種調用方式 - 非尾隨閉包 funcB(str: "zhuhoulin", closure_A: { }, closure_B: { }) - 尾隨閉包 funcB(str: "zhuhoulin", closure_A: { }) { } 對比上面兩種方式,只有最后一個閉包才有成為尾隨閉包的這個權利 - 總結-什么是尾隨閉包 - 閉包作為函數的參數,并且這個參數是最后一個參數 - 這個閉包包含的代碼塊不可不用寫在"()"之內,而是寫在的"()"之后的"{}"之內
值捕獲-Capturing Values
閉包是引用類型-Closures Are Reference Types
-
非逃逸閉包-Nonescaping Closures
- 官方對非逃逸閉包的描述: “A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns.” 其實意思很簡單,如果一個函數或者方法中,有一個閉包式的參數,如果這個閉包式的參數在return之前已經被執行,那么這個閉包就是非逃逸閉包 - 官方例子: func someFunctionWithNonescapingClosure(closure: @noescape () -> Void) { closure() } 很簡單,閉包在函數中執行了就是非逃逸閉包,沒有執行就是逃逸閉包 - 個人使用的一個栗子: