iOS開發(fā) - 「Swift 學(xué)習(xí)」Swift中的循環(huán)強(qiáng)引用

Swift中使用(RAC)自動(dòng)引用計(jì)數(shù)機(jī)制,實(shí)例賦值給屬性、常量或變量,都會(huì)創(chuàng)建此實(shí)例的強(qiáng)引用,只要強(qiáng)引用存在,實(shí)例是不允許被銷毀的

實(shí)例間的循環(huán)強(qiáng)引用

當(dāng)兩個(gè)實(shí)例互相保持對(duì)方的強(qiáng)引用,并讓對(duì)方不會(huì)被銷毀,就會(huì)造成循環(huán)強(qiáng)引用。
例:

    class School{
        let classNum:Int
        init(classNum:Int){
            self.classNum = classNum
        }
        var student:Student?
        deinit{
            print("\(classNum)班,被析構(gòu)")
        }
    }
    class Student {
        var name:String = ""
        init(name:String){
            self.name = name
        }
        var school:School?
        init(closure: @escaping (_ student: Student) -> Void) {
            closure(self)
        }
        deinit{
            print("\(name)" + "被析構(gòu)")
        }
    }

調(diào)用

//定義初始化為nil的屬性
    var  xiaoMing:Student?
    var  sunMiddleSchool:School?
    func setValue(){
        //賦值
        xiaoMing = Student(name: "小明")
        sunMiddleSchool = School(classNum: 3)
        
        //對(duì)可選實(shí)例xiaoMing和sunMiddleSchool中的school、student屬性賦值(此時(shí)會(huì)造成循環(huán)強(qiáng)引用)
        xiaoMing?.school = sunMiddleSchool
        sunMiddleSchool?.student = xiaoMing
        
        //釋放實(shí)例xiaoMing和sunMiddleSchool變量持有的強(qiáng)引用時(shí),引用計(jì)數(shù)不會(huì)降為0,實(shí)例也并不會(huì)被ARC銷毀,析構(gòu)函數(shù)也并不會(huì)被調(diào)用
        //此時(shí)實(shí)例間的循環(huán)強(qiáng)引用阻止了實(shí)例銷毀,并會(huì)造成內(nèi)存泄漏
        xiaoMing = nil
        sunMiddleSchool = nil
    }

解決實(shí)例間的循環(huán)強(qiáng)引用

Swift中提供了弱引用、無(wú)主引用兩種方法解決使用類的屬性遇到的循環(huán)強(qiáng)引用問(wèn)題

弱引用

對(duì)于生命周期中會(huì)變?yōu)閚il的實(shí)例使用弱引用,通過(guò)在定義類屬性語(yǔ)句前添加“weak”關(guān)鍵字聲明弱引用類屬性

class School{
        let classNum:Int
        init(classNum:Int){
            self.classNum = classNum
        }
        //使用"weak"關(guān)鍵字定義弱引用實(shí)例屬性
        weak  var student:Student?
        deinit{
            print("\(classNum)班,被析構(gòu)")
        }
    }
    class Student {
        var name:String = ""
        init(name:String){
            self.name = name
        }
        //使用"weak"關(guān)鍵字定義弱引用實(shí)例屬性
        weak  var school:School?
        init(closure: @escaping (_ student: Student) -> Void) {
            closure(self)
        }
        deinit{
            print("\(name)" + "被析構(gòu)")
        }
    }

調(diào)用

//定義初始化為nil的屬性
    var  xiaoMing:Student?
    var  sunMiddleSchool:School?
    func setValue(){
        //賦值
        xiaoMing = Student(name: "小明")
        sunMiddleSchool = School(classNum: 3)
        
        //對(duì)可選實(shí)例xiaoMing和sunMiddleSchool中的school、student屬性賦值(此時(shí)形成的是循環(huán)弱引用)
        xiaoMing?.school = sunMiddleSchool
        sunMiddleSchool?.student = xiaoMing
        
        //釋放實(shí)例xiaoMing和sunMiddleSchool變量持有的弱引用時(shí),引用計(jì)數(shù)降為0,實(shí)例會(huì)被ARC銷毀,析構(gòu)函數(shù)會(huì)被調(diào)用
        xiaoMing = nil
        sunMiddleSchool = ni
    }

調(diào)用結(jié)果

小明被析構(gòu)
3班,被析構(gòu)

無(wú)主引用

對(duì)于初始化賦值后再也不會(huì)被賦值為nil的實(shí)例,使用無(wú)主引用。

閉包引起的循環(huán)強(qiáng)引用

例:

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 {
        print("\(name) is being deinitialized")
    }
}

調(diào)用

func HTMLElementFunc(){
    var paragraph:HTMLElement? = HTMLElement(name: "p", text: "hello,world")
    //HTMLElement 類產(chǎn)生了類實(shí)例和 asHTML 默認(rèn)值的閉包之間的循環(huán)強(qiáng)引用
    print(paragraph!.asHTML())
    paragraph = nil
}

調(diào)用結(jié)果

<p>hello,world</p>

從調(diào)用結(jié)果看出:析構(gòu)函數(shù)并未被調(diào)用說(shuō)明paragraph實(shí)例并沒有被釋放,實(shí)例的 asHTML 屬性持有閉包的強(qiáng)引用。但是,閉包在其閉包體內(nèi)使用了self(引用了self.name和self.text),因此閉包捕獲了self,這意味著閉包又反過(guò)來(lái)持有了HTMLElement實(shí)例的強(qiáng)引用。這樣兩個(gè)對(duì)象就產(chǎn)生了循環(huán)強(qiáng)引用.

解決閉包引起的循環(huán)強(qiáng)引用:在定義閉包時(shí)同時(shí)定義捕獲列表作為閉包的一部分,通過(guò)這種方式可以解決閉包和類實(shí)例之間的循環(huán)強(qiáng)引用。

弱引用

1.當(dāng)閉包和捕獲的實(shí)例總是互相引用時(shí)并且總是同時(shí)銷毀時(shí),將閉包內(nèi)的捕獲定義為無(wú)主引用。
2.相反的,當(dāng)捕獲引用有時(shí)可能會(huì)是nil時(shí),將閉包內(nèi)的捕獲定義為弱引用。
3.如果捕獲的引用絕對(duì)不會(huì)置為nil,應(yīng)該用無(wú)主引用,而不是弱引用

無(wú)主引用

通過(guò)在定義閉包時(shí)同時(shí)定義捕獲列表[unowned self] in作為閉包的一部分,來(lái)解決閉包和類實(shí)例之間的循環(huán)強(qiáng)引用。

class HTMLElement {
    let name:String
    let text:String?
    
    lazy var asHTML:() -> String = {
    C  //定義閉包時(shí)同時(shí)定義捕獲列表
        [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")
    }
}

調(diào)用

func HTMLElementFunc(){
    var paragraph:HTMLElement? = HTMLElement(name: "p", text: "hello,world")
    print(paragraph!.asHTML())
    paragraph = nil
}

調(diào)用結(jié)果

<p>hello,world</p>
p is being deinitialized

根據(jù)調(diào)用結(jié)果分析:此時(shí)的析構(gòu)函數(shù)被調(diào)用,實(shí)例paragraph已被釋放銷毀

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

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

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