在定義閉包時(shí)同時(shí)定義捕獲列表作為閉包的一部分,通過這種方式可以解決閉包和類實(shí)例之間的循環(huán)強(qiáng)引用。捕獲列表定義了閉包體內(nèi)捕獲一個(gè)或者多個(gè)引用類型的規(guī)則。跟解決兩個(gè)類實(shí)例間的循環(huán)強(qiáng)引用一樣,聲明每個(gè)捕獲的引用為弱引用或無主引用,而不是強(qiáng)引用。應(yīng)當(dāng)根據(jù)代碼關(guān)系來決定使用弱引用還是無主引用。
注意:
Swift 有如下要求:只要在閉包內(nèi)使用self
的成員,就要用self.someProperty
或者self.someMethod
(而不只是someProperty
或someMethod
)。這提醒你可能會(huì)不小心就捕獲了self
。
捕獲列表中的每個(gè)元素都是由weak
或者unowned
關(guān)鍵字和實(shí)例的引用(如self
或someInstance
)成對(duì)組成。每一對(duì)都在方括號(hào)中,通過逗號(hào)分開。
捕獲列表放置在閉包參數(shù)列表和返回類型之前:
@lazy var someClosure: (Int, String) -> String = {
[unowned self] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
如果閉包沒有指定參數(shù)列表或者返回類型,則可以通過上下文推斷,那么可以捕獲列表放在閉包開始的地方,跟著是關(guān)鍵字in
:
@lazy var someClosure: () -> String = {
[unowned self] in
// closure body goes here
}
當(dāng)閉包和捕獲的實(shí)例總是互相引用時(shí)并且總是同時(shí)銷毀時(shí),將閉包內(nèi)的捕獲定義為無主引用。
相反的,當(dāng)捕獲引用有時(shí)可能會(huì)是nil
時(shí),將閉包內(nèi)的捕獲定義為弱引用。弱引用總是可選類型,并且當(dāng)引用的實(shí)例被銷毀后,弱引用的值會(huì)自動(dòng)置為nil
。這使我們可以在閉包內(nèi)檢查它們是否存在。
注意:
如果捕獲的引用絕對(duì)不會(huì)置為nil
,應(yīng)該用無主引用,而不是弱引用。
前面的HTMLElement
例子中,無主引用是正確的解決循環(huán)強(qiáng)引用的方法。這樣編寫HTMLElement
類來避免循環(huán)強(qiáng)引用:
class HTMLElement {
let name: String
let text: String?
@lazy var asHTML: () -> String = {
[unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
println("\(name) is being deinitialized")
}
}
上面的HTMLElement
實(shí)現(xiàn)和之前的實(shí)現(xiàn)一致,只是在asHTML
閉包中多了一個(gè)捕獲列表。這里,捕獲列表是[unowned self]
,表示“用無主引用而不是強(qiáng)引用來捕獲self
”。
和之前一樣,我們可以創(chuàng)建并打印HTMLElement
實(shí)例:
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML())
// prints "<p>hello, world</p>"
使用捕獲列表后引用關(guān)系如下圖所示:
這一次,閉包以無主引用的形式捕獲self
,并不會(huì)持有HTMLElement
實(shí)例的強(qiáng)引用。如果將paragraph
賦值為nil
,HTMLElement
實(shí)例將會(huì)被銷毀,并能看到它的析構(gòu)函數(shù)打印出的消息。
paragraph = nil
// prints "p is being deinitialized"