要理解@escaping,首先需要理解closure, 要理解closure,首先理解匿名函數。
先理解匿名函數
要在Swift中構造匿名函數,需要:
- 創建函數體,包括花括號,但是不需要函數聲明
- 如果必要,將函數的參數列表與返回類型作為花括號中的第一行,后跟關鍵字in.
例子1: 將以下函數變成匿名函數:
func whatToAnimate(){
self.myButton.frame.origin.y += 20
}
匿名函數版本:
{
() -> () in
self.myButton.frame.origin.y += 20
}
例子2: 有參數的具名函數:
func whatToDoLater(finished: Bool){
print("finished: \(finished)")
}
匿名函數版本:
{
(finished: Bool) -> () in
print("finished: \(finished)")
}
可省略的地方:
- 省略返回類型
- 沒有參數可以省略 in 這一樣
- 省略參數類型
- 省略圓括號,這個是如果就一個參數,并且我們編譯器可以推斷出其類型的話
- 省略 in, 直接用名字比如 $0
- 省略參數名,用_代替
- 省略函數實參標簽(又叫尾函數
...
一路省略下來,匿名函數真是節約。
再理解閉包
理解匿名函數之后,我們來看閉包的表達式:
{ (parameters) -> return type in
statements
}
這就是匿名函數啊,而我們叫它閉包則是因為:
閉包可以捕獲和存儲其所在上下文中任意常量和變量的引用。 這就是所謂的閉合并包裹著這些常量和變量,俗稱閉包。
而在這一篇中,我們可以一步一步看到如何從
reversed = sorted(names, { (s1: String, s2: String) -> Bool in
return s1 > s2
})
利用省略到:
reversed = sorted(names, >)
再利用 trailling closure的特性,可以這樣:
reversed = sorted(names) { $0 > $1 }
閉包當然還有更好的例子,因為我們來看它的一些特性。
Escaping Closure(逃逸閉包
如果一個閉包被作為一個參數傳遞給一個函數,并且在函數return之后才被喚起執行,那么這個閉包是逃逸閉包。
好抽象的描述,這篇文章寫得不錯:
不逃逸閉包的生命周期:
- Pass a closure into a function
- The function runs the closure (or not)
- The function returns
然后這個closure就死掉了。
而逃逸閉包則會一直存在,沒有因為function被kill而死掉,所以我們叫這個閉包為逃逸閉包(?
在以下的兩種情況,我們需要使用 escaping closure:
- 異步execution,并不能說函數return了就把closure kill掉,因為這個closure可能還沒有執行完畢
- 存儲,如果任何全局變量都有一些些存儲存在,那么這個closure也被逃逸掉了。
在 Swift 1 和 2中, closure by default 是 escaping的,所以我們需要用 @noescape 來mark.
在 Swift 3中,closure by default是non-escaping,我們需要用@escaping 來mark
In Swift 3, closure parameters are non-escaping by default; you can use the new @escaping attribute if this isn’t what you want. Non-escaping closures passed in as arguments are guaranteed to not stick around once the function returns.
而一般情況下,弱??就跟著Xcode的提示走?
參考:
- <iOS 9 Programming Fundamentals with Swift> by Matt Neuburg
- 閉包
- Escaping and Nonescaping Closures in Swift 3