Swift解決【閉包引起的迴圈強引用】

小豬熊發表於2017-12-13

迴圈強引用還會發生在當你將一個閉包賦值給類例項的某個屬性,並且這個閉包體中又使用了這個類例項時。這個閉包體中可能訪問了例項的某個屬性,例如self.someProperty,或者閉包中呼叫了例項的某個方法,例如self.someMethod()。這兩種情況都導致了閉包“捕獲”self,從而產生了迴圈強引用。

解決閉包引起的迴圈強引用

在定義閉包時同時定義捕獲列表作為閉包的一部分,通過這種方式可以解決閉包和類例項之間的迴圈強引用。捕獲列表定義了閉包體內捕獲一個或者多個引用型別的規則。跟解決兩個類例項間的迴圈強引用一樣,宣告每個捕獲的引用為弱引用或無主引用,而不是強引用。應當根據程式碼關係來決定使用弱引用還是無主引用。

注意 Swift 有如下要求:只要在閉包內使用self的成員,就要用self.someProperty或者self.someMethod()(而不只是somePropertysomeMethod())。這提醒你可能會一不小心就捕獲了self

定義捕獲列表

捕獲列表中的每一項都由一對元素組成,一個元素是weakunowned關鍵字,另一個元素是類例項的引用(例如self)或初始化過的變數(如delegate = self.delegate!)。這些項在方括號中用逗號分開。

如果閉包有引數列表和返回型別,把捕獲列表放在它們前面:

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // 這裡是閉包的函式體
}
複製程式碼

如果閉包沒有指明引數列表或者返回型別,即它們會通過上下文推斷,那麼可以把捕獲列表和關鍵字in放在閉包最開始的地方:

lazy var someClosure: Void -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // 這裡是閉包的函式體
}
複製程式碼
弱引用和無主引用

在閉包和捕獲的例項總是互相引用並且總是同時銷燬時,將閉包內的捕獲定義為無主引用

相反的,在被捕獲的引用可能會變為nil時,將閉包內的捕獲定義為弱引用。弱引用總是可選型別,並且當引用的例項被銷燬後,弱引用的值會自動置為nil。這使我們可以在閉包體內檢查它們是否存在。

注意 如果被捕獲的引用絕對不會變為nil,應該用無主引用,而不是弱引用。

案例

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: Void -> 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 {
        print("\(name) is being deinitialized")
    }

}
複製程式碼

捕獲列表是[unowned self],表示“將self捕獲為無主引用而不是強引用”。

建立並列印HTMLElement例項:

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// 列印 “<p>hello, world</p>”
複製程式碼

使用捕獲列表後引用關係如下圖所示:

Swift解決【閉包引起的迴圈強引用】
這一次,閉包以無主引用的形式捕獲self,並不會持有HTMLElement例項的強引用。如果將paragraph賦值為nilHTMLElement例項將會被銷燬,並能看到它的解構函式列印出的訊息:

paragraph = nil
// 列印 “p is being deinitialized”
複製程式碼

引自:http://www.piggybear.net/?p=690

相關文章