Table of Contents
<a id="1"></a>前言
Closure 是一個函數(shù)塊肺稀,在 Swift 3.0 的官方文檔里有詳細(xì)的說明痕钢。從 Swift 2.3 到 Swift 3.0 ,Closure 也有了一些變化。本文主要通過一些例子,談?wù)勛约旱睦斫狻?/p>
<a id="2"></a>Closure 的表達(dá)形式
Closure 其實(shí)就是一段函數(shù)随闪。當(dāng)一個函數(shù)的使用范圍比較小搀崭,沒有必要為它進(jìn)行明確的冗長的聲明粗卜,這時(shí)候,就可以用 Closure 來實(shí)現(xiàn)這個函數(shù)的功能纳击,使代碼更加緊湊续扔,清晰。
<a id="21"></a>排序函數(shù)
在官方文檔里焕数,通過 sorted(by:)
函數(shù)來描述 Closure 的運(yùn)作過程纱昧,一個 closure 作為 sorted(by:)
的參數(shù)傳入,最終達(dá)到利用這個 closure 排序進(jìn)行的目的堡赔。但是 sorted(by:)
函數(shù)具體的實(shí)現(xiàn)并沒有給出识脆,因此,對于初學(xué)者來說善已,并不清楚 sorted(by:)
函數(shù)對 closure 做了什么灼捂。在這里就通過自己的一個例子來說明,Closure 到底是怎么運(yùn)作的换团。
下面為 Array
類自定義了一個排序函數(shù)悉稠,這個函數(shù)以一個函數(shù)(或者 closure )為參數(shù),最終返回一組元素艘包。參數(shù)列表里的函數(shù)需要有兩個參數(shù)的猛,并返回 Bool
值。
extension Array{
func mySort(clo:(_ s1: String, _ s2: String)-> Bool)-> [Element]{
var tempArray = self
for i in 0...tempArray.count - 1{
for j in i...tempArray.count - 1{
if closure(tempArray[i] as! String, tempArray[j] as! String){
let temp = tempArray[i]
tempArray[i] = tempArray[j]
tempArray[j] = temp
}
}
}
return tempArray
}
}
上面這段代碼大致模擬了 closure 在函數(shù)里的使用過程想虎。在這個函數(shù)中卦尊,通過調(diào)用參數(shù)表里的 clo 函數(shù),利用 clo 函數(shù)返回的值對數(shù)組元素進(jìn)行操作舌厨,最后返回一組元素岂却。
在函數(shù)外,我們可以明確地定義一個比較函數(shù):
func numSort(n1:String, n2:String)->Bool{
return n1<n2
}
這樣我們就能把這個比較函數(shù) numSort:
傳入排序函數(shù) mySort(clo:)
:
let num = ["a","b","c","d"]
let number = num.mySort(clo: numSort)
// number is equal to ["d", "c", "b", "a"].
在上面這段代碼中邓线,mySort(clo:)
函數(shù)被傳入了一個函數(shù)作為參數(shù)淌友。因此,在完成這個排序功能時(shí)骇陈,需要額外定義一個比較函數(shù)震庭,然后作為參數(shù)傳入。對與比較函數(shù)這樣短小的函數(shù)你雌,額外的定義顯得有些繁瑣器联,不夠簡練二汛,因此 Swift 提供了 Closure 來簡化這個過程:
let numberb = num1.mySort{
(a,b)->Bool in
return a<b
}
// numberb is equal to number above.
上面這段代碼利用了一個簡單的 closure 替換了之前的比較函數(shù),同樣實(shí)現(xiàn)了排序的功能拨拓。在這種情況下肴颊,就不需要額外定義比較函數(shù)了。
需要注意的是渣磷,當(dāng) closure 作為函數(shù)的最后一個參數(shù)時(shí)婿着,在調(diào)用函數(shù)時(shí),可以省去小括號醋界,并把 closure 寫在外面竟宋。官方文檔里稱之為
Trailing Closures
。
上面的例子展示了從函數(shù)到 closure 的替換形纺,對于 closure 的表達(dá)還能進(jìn)行簡化丘侠,直到:
let numberc = num.mySort(clo:<)
// numberc is equal to number and numberb above.
具體的簡化過程及解釋請參考官方文檔。
<a id="3"></a>@autoclosure 和 @escaping
@autoclosure
和 @escaping
可以用來標(biāo)記 closure 參數(shù)的類型逐样。
New in Xcode 8 beta – Swift and Apple LLVM Compilers: Swift Language
The @noescape and @autoclosure attributes must now be written before the parameter type instead of before the parameter name. [SE-0049]
Swift 3: closure parameters attributes are now applied to the parameter type, and not the parameter itself
注意蜗字,這里指的是標(biāo)記參數(shù)的類型,在 Swift 3 之前脂新,它們是用來標(biāo)記參數(shù)的挪捕。
Swift 2.3 及之前版本:
func doSomething(withParameter parameter: Int, @escaping completion: () -> ()) {
// ...
}
Swift 3.0:
func doSomething(withParameter parameter: Int, completion: @escaping () -> ()) {
// ...
}
用 @autoclosure
標(biāo)記 clousre 參數(shù)的類型后,在函數(shù)調(diào)用的時(shí)候就可以去掉 closure 的花括號戏羽,把 closure 以其返回值的形式傳入函數(shù)中担神,以下是不帶 @autoclosure
和帶 @autoclosure
的參數(shù)類型及其使用:
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!”
// 摘錄來自: Apple Inc. “The Swift Programming Language (Swift 3)”。 iBooks.
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!”
// 摘錄來自: Apple Inc. “The Swift Programming Language (Swift 3)”始花。 iBooks.
@escaping
標(biāo)記在 Swift 3 之前是沒有的妄讯,在 Swift 2.3 中,只有 @noescaping
酷宵。
New in Xcode 8 beta 6 - Swift Compiler: Swift Language
Closure parameters are non-escaping by default, rather than explicitly being annotated with @noescape. Use @escaping to indicate that a closure parameter may escape. @autoclosure(escaping) is now written as @autoclosure @escaping. The annotations @noescape and @autoclosure(escaping) are deprecated. (SE-0103)
也就是說亥贸,現(xiàn)在 closure 作為函數(shù)的參數(shù),默認(rèn)是 @noescaping 類型的浇垦。
@escaping
標(biāo)記表示 closure 在函數(shù)運(yùn)行結(jié)束后再執(zhí)行炕置,而 @noescaping
標(biāo)記表示 closure 必須在函數(shù)運(yùn)行結(jié)束前執(zhí)行。一個常見的例子是常見的 completion handle 男韧,它們在函數(shù)運(yùn)行完成后才執(zhí)行朴摊。
UIView
中的 animate 函數(shù),它的 completion handle 就是 @escaping 的此虑。在通常的書寫代碼的界面中甚纲,并沒有顯式表出來:
class func animate(withDuration duration: TimeInterval, animations: () -> Void, completion: ((Bool) -> Void)? = nil)
但在定義中,可以看出:
open class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Swift.Void, completion: (@escaping (Bool) -> Swift.Void)? = nil)
需要注意的是朦前,當(dāng) closure 的類型用 @escaping
標(biāo)記之后介杆,在 closure 內(nèi)使用類的屬性或方法時(shí)鹃操,需要用 self
標(biāo)明。
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
// 摘錄來自: Apple Inc. “The Swift Programming Language (Swift 3)”春哨。 iBooks.
<a id="4"></a>Closure playground
關(guān)于 Closure 荆隘,這里有一個 Swift playground ,里面有一些例子可以參考赴背。
附上我的Github:LinShiwei (Lin Shiwei) · GitHub