前面我們看到了循環(huán)強(qiáng)引用環(huán)是在兩個類實例屬性互相保持對方的強(qiáng)引用時產(chǎn)生的,還知道了如何用弱引用和無主引用來打破循環(huán)強(qiáng)引用。
循環(huán)強(qiáng)引用還會發(fā)生在當(dāng)你將一個閉包賦值給類實例的某個屬性,并且這個閉包體中又使用了實例。這個閉包體中可能訪問了實例的某個屬性,例如self.someProperty,或者閉包中調(diào)用了實例的某個方法,例如self.someMethod。這兩種情況都導(dǎo)致了閉包 “捕獲" self,從而產(chǎn)生了循環(huán)強(qiáng)引用。
循環(huán)強(qiáng)引用的產(chǎn)生,是因為閉包和類相似,都是引用類型。當(dāng)你把一個閉包賦值給某個屬性時,你也把一個引用賦值給了這個閉包。實質(zhì)上,這跟之前的問題是一樣的-兩個強(qiáng)引用讓彼此一直有效。但是,和兩個類實例不同,這次一個是類實例,另一個是閉包。
Swift 提供了一種優(yōu)雅的方法來解決這個問題,稱之為閉包占用列表(closuer capture list)。同樣的,在學(xué)習(xí)如何用閉包占用列表破壞循環(huán)強(qiáng)引用之前,先來了解一下循環(huán)強(qiáng)引用是如何產(chǎn)生的,這對我們是很有幫助的。
下面的例子為你展示了當(dāng)一個閉包引用了self后是如何產(chǎn)生一個循環(huán)強(qiáng)引用的。例子中定義了一個叫HTMLElement的類,用一種簡單的模型表示 HTML 中的一個單獨的元素:
class HTMLElement {
let name: String
let text: String?
@lazy var asHTML: () -> String = {
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類定義了一個name屬性來表示這個元素的名稱,例如代表段落的"p",或者代表換行的"br"。HTMLElement還定義了一個可選屬性text,用來設(shè)置和展現(xiàn) HTML 元素的文本。
除了上面的兩個屬性,HTMLElement還定義了一個lazy屬性asHTML。這個屬性引用了一個閉包,將name和text組合成 HTML 字符串片段。該屬性是() -> String類型,或者可以理解為“一個沒有參數(shù),返回String的函數(shù)”。
默認(rèn)情況下,閉包賦值給了asHTML屬性,這個閉包返回一個代表 HTML 標(biāo)簽的字符串。如果text值存在,該標(biāo)簽就包含可選值text;如果text不存在,該標(biāo)簽就不包含文本。對于段落元素,根據(jù)text是"some text"還是nil,閉包會返回"<p>some text</p>"或者"<p />"。
可以像實例方法那樣去命名、使用asHTML屬性。然而,由于asHTML是閉包而不是實例方法,如果你想改變特定元素的 HTML 處理的話,可以用自定義的閉包來取代默認(rèn)值。
注意:
asHTML聲明為lazy屬性,因為只有當(dāng)元素確實需要處理為HTML輸出的字符串時,才需要使用asHTML。也就是說,在默認(rèn)的閉包中可以使用self,因為只有當(dāng)初始化完成以及self確實存在后,才能訪問lazy屬性。
HTMLElement類只提供一個構(gòu)造函數(shù),通過name和text(如果有的話)參數(shù)來初始化一個元素。該類也定義了一個析構(gòu)函數(shù),當(dāng)HTMLElement實例被銷毀時,打印一條消息。
下面的代碼展示了如何用HTMLElement類創(chuàng)建實例并打印消息。
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
println(paragraph!.asHTML())
// prints"hello, world"
注意:
上面的paragraph變量定義為可選HTMLElement,因此我們可以賦值nil給它來演示循環(huán)強(qiáng)引用。
不幸的是,上面寫的HTMLElement類產(chǎn)生了類實例和asHTML默認(rèn)值的閉包之間的循環(huán)強(qiáng)引用。循環(huán)強(qiáng)引用如下圖所示:

實例的上一篇:Swift條件語句下一篇:Swift帶標(biāo)簽的語句