(二十四)[Swift]循環(huán)引用

1.什么是循環(huán)引用

兩個對象通過屬性互相強引用對方,導致無法正常釋放

情境代碼

class City{
    var country : Country?
    deinit{
        print("city is deinit")
    }
}
class Country {
    var capital : City?
    deinit{
        print("country is deinit")
    }
}

不會循環(huán)引用

var city:City? = City()
var country:Country? = Country()
city!.country = country!
city = nil
country = nil
//city country此時釋放會正常打印
//city is deinit
//country is deinit

會導致循環(huán)引用

var city2:City? = City()
var country2:Country? = Country()

city2!.country = country2!
country2!.capital=city2!
city2 = nil
country2 = nil
//city country此時設為nil,沒有任何打印,說明并不會正常釋放
兩個強指針指向?qū)Ψ綄е禄ハ酂o法釋放

2.在optional類型的屬性中加上weak關鍵字

weak關鍵詞不會使引用計數(shù)+1,同時如果所指向?qū)ο蟊会尫?,則自動設為nil

class City2{
   weak var country : Country2?
    deinit{
        print("city2 is deinit")
    }
}
class Country2 {
    var capital : City2?
    deinit{
        print("country2 is deinit")
    }
}

var city2:City2? = City2()
var country2:Country2? = Country2()
country2?.capital=city2!
city2?.country=country2

city2?.country == nil  //false
country2 = nil //打印 country is deinit
city2?.country == nil  //true
city2 = nil   //打印 city2 is deinit
有一方為weak類型屬性的示意圖

3.在非optional類型的屬性中加上unown關鍵字

unown關鍵字只是不會使引用計數(shù)+1,所指向的對象被釋放后,此屬性不能再被訪問,否則會報錯

class City3{
    var country : Country3?
    deinit{
        print("city3 is deinit")
    }
}
class Country3 {
    unowned var capital : City3
    init(capital:City3){
        self.capital = capital;
    }
    deinit{
        print("country3 is deinit")
    }
}

var city3:City3? = City3()
var country3:Country3? = Country3(capital:city3!)

city3?.country=country3

  //打印 city2 is deinit
country3?.capital == nil  //false
city3 = nil //打印 city3 is deinit
//country3?.capital == nil  //報錯
country3 = nil //打印 country3 is deinit
有一方為unowned類型屬性的示意圖

所以應該根據(jù)實際情況來決定是使用unowned關鍵字還是weak關鍵字

4.closure中的循環(huán)引用

class HTMLElement{
    var text:String
    init(text:String){
        self.text=text
    }
    lazy var asHTML : Void -> String = { 
        return "<text>\(self.text)</text>"
    }
    deinit{
        print("HTMLElement 被 釋放了")
    }
}

看起來并沒有什么問題,編譯器也沒有報錯
但是

var e :HTMLElement?  = HTMLElement(text:"haha")
e?.asHTML()
e = nil //并沒有任何打印,說明發(fā)生了循環(huán)引用

為什么會發(fā)生循環(huán)引用?

因為closure實際上也是一個對象,并且會捕獲其所用到的變量。并默認使用一個強類型的指針,指向其用到的變量。這里發(fā)生的情況,如圖

HTMLElement與Closure循環(huán)引用示意圖

那么如何將其中的一個強類型指針改變成weak或者unowned呢?改變asHTML屬性的類型為weak或者unowned嗎?

顯然不行,weak 和 unowned 只適用于class和protocol類型的屬性

那只有修改closure中self的指針類型了
這樣,在closure中加上[unowned self]的說明

class HTMLElement{
    var text:String
    init(text:String){
        self.text=text
    }
    lazy var asHTML : Void -> String = { [unowned self] in
        return "<text>\(self.text)</text>"
    }
    deinit{
        print("HTMLElement 被 釋放了")
    }
}

如果是多個說明的話用逗號隔開比如

[unowned self,weak other]

這個說明應該放在參數(shù)列表的前面

  lazy var asHTML : Void -> String = { [unowned self] () -> String in
        return "<text>\(self.text)</text>"
    }

加上說明再來測試一下

var e :HTMLElement? = HTMLElement(text:"haha")
e?.asHTML()
e = nil //打印HTMLElement 被 釋放了 

說明正常釋放了

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容