循環引用

首先一張圖解釋一下循環引用

Swift問題解決方案

爲了解決這個問題,swift提供了三種基本的解決方案,如下:

弱引用(Weak Reference)

如果被指向的實例有可能爲nil,則使用弱引用

無主引用(Unowned Reference)

如果被指向的實例不爲nil,則使用無主引用

捕獲列表(Capture List)

如果在類屬性使用閉包時,且閉包體內引用當前實例self而產生強引用環時,則使用捕獲列表

下面對這三種方法和使用的情況分別做解釋說明

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //強引用例子
        strongReferenceMethod()
        //解決方法一,弱引用例子,被指向的實例有可能爲nil的時候
        weakReferenceMethod()
        //解決方法二,無主引用,被指向的實例不能爲nil的時候
        unownedReferenceMethod()
        //解決方法三,捕獲列表.如果在類屬性使用閉包時,且閉包內引用當前實例self而產生強引用環時,則使用捕獲列表
        captureListMethod()
    }
}

func strongReferenceMethod() {
    var man:Male? = Male(name: "李雷")
    var woman:Female? = Female(name: "韓梅梅")
    man!.girlFriend = woman
    woman!.boyFriend = man
    debugPrint("韓妹妹和李雷完成了人生使命,我們告訴arc這兩個實例不再使用了。")
    debugPrint("woman 實例釋放前,man.girlFriend 的值:\(man!.girlFriend)") //"woman 實例釋放前,man.girlFriend 的值:Optional(StrongReferenceCycle.Female)"
    woman = nil
    debugPrint("woman 實例釋放後,man.girlFriend 的值:\(man!.girlFriend)")
    man = nil //"woman 實例釋放後,man.girlFriend 的值:Optional(StrongReferenceCycle.Female)"
    //可以看到並沒有調用對應的dealloc銷燬方法
}

func weakReferenceMethod() {
    var man:Male2? = Male2(name: "李雷2")
    var woman:Female2? = Female2(name: "韓梅梅2")
    man!.girlFriend = woman
    woman!.boyFriend = man
    debugPrint("韓妹妹2和李雷2完成了人生使命,我們告訴arc這兩個實例不再使用了。")
    debugPrint("woman 實例釋放前,man.girlFriend 的值:\(man!.girlFriend)")//"woman 實例釋放前,man.girlFriend 的值:Optional(StrongReferenceCycle.Female2)"
    debugPrint("man 實例釋放前,woman.boyFriend 的值:\(woman!.boyFriend)")//"man 實例釋放前,woman.boyFriend 的值:Optional(StrongReferenceCycle.Male2)"
    woman = nil //"女人2實例被銷燬"
    debugPrint("woman 實例釋放後,man.girlFriend 的值:\(man!.girlFriend)")//"woman 實例釋放後,man.girlFriend 的值:nil"
    man = nil //"男人2實例被銷燬"
    debugPrint("man 實例釋放後,woman.boyFriend  的值:\(woman?.boyFriend )")//"man 實例釋放後,woman.boyFriend  的值:nil"這裏需要使用woman?來進行解包,否則因爲woman已經是nil了,如果強制解包就會報錯
    //這裏可以看到swift會主動保護弱引用的值。如man實例中的弱引用girlfriend屬性,他指向的是woman屬性,所以當woman實例釋放,則girlfriend值會被swift重置爲nil
}

func unownedReferenceMethod() {
    //無主引用總是假定其引用對象一直存在(有值),因而無主引用不能修飾可選類型。使用場景1.其中一個類實例中國的屬性爲可選類型,即其值可以爲nil,而另一個實例中的屬性爲非可選類型,即不能爲nil。場景2.兩個類實例中的屬性,一旦初始化後,都不能爲nil
    var stark: Adult?
    stark = Adult(name: "柰徳.斯塔克")
    stark!.child = Child(name: "羅伯.斯塔克", guardian: stark!)
    stark = nil
}

func captureListMethod() {
    var weatherReport: WeatherReport? = WeatherReport(location: "成都", weather: "多雲")
    debugPrint("\(weatherReport!.reports())") //"成都的天氣預報是:多雲"
    weatherReport = nil //但是這個執行後並沒有調用dealloc方法釋放
    
    var weatherReport2: WeatherReport2? = WeatherReport2(location: "成都", weather: "多雲")
    debugPrint("\(weatherReport2!.reports())") //"成都的天氣預報是:多雲"
    weatherReport2 = nil //"成都的天氣預報實例被銷燬!" 這裏可以看到正確執行了dealloc方法
}

class Male {
    let name: String
    var girlFriend: Female? //這裏互相引用
    init(name: String) {
        self.name = name
    }
    deinit {
        debugPrint("男人實例被銷燬")
    }
}

class Female {
    let name: String
    var boyFriend: Male? //這裏互相引用
    init(name: String) {
        self.name = name
    }
    deinit {
        debugPrint("女人實例被銷燬")
    }
}

class Male2 {
    let name: String
    weak var girlFriend: Female2? //弱引用必須是變量,因爲有可能在運行過程中值被修改了
    init(name: String) {
        self.name = name
    }
    deinit {
        debugPrint("男人2實例被銷燬")
    }
}

class Female2 {
    let name: String
    weak var boyFriend: Male2? //弱引用必須是變量,因爲有可能在運行過程中值被修改了
    init(name: String) {
        self.name = name
    }
    deinit {
        debugPrint("女人2實例被銷燬")
    }
}

class Adult {
    let name: String
    init(name: String) {
        self.name = name
    }
    var child: Child?
    deinit {
        debugPrint("adult\(name)被銷燬!")
    }
}

class Child {
    let name: String
    unowned var guardian:Adult //不能爲nil的實例標註爲unowned類型
    init(name: String, guardian: Adult) {
        self.name = name
        self.guardian = guardian
    }
    deinit {
        debugPrint("child\(name)被銷燬!")
    }
}

class WeatherReport {
    let location: String
    let weather: String
    var temperature: Int?
    //因爲該計算屬性使用到閉包且閉包中使用了self,所以必須是惰性屬性.因爲此時self和其他使用到的屬性有可能還沒有初始化完成就執行了reports屬性內部的操作
    lazy var reports: ()->String = {
        if self.temperature != nil {
            return "\(self.location)的天氣預報是: \(self.weather), 氣溫是\(self.temperature)"
        } else {
            return "\(self.location)的天氣預報是:\(self.weather)"
        }
    }
    
    init(location: String, weather: String) {
        self.location = location
        self.weather = weather
    }
    
    deinit {
        debugPrint("\(location)的天氣預報實例被銷燬!")
    }
    
}

class WeatherReport2 {
    let location: String
    let weather: String
    var temperature: Int?
    //因爲該計算屬性使用到閉包且閉包中使用了self,所以必須是惰性屬性.因爲此時self和其他使用到的屬性有可能還沒有初始化完成就執行了reports屬性內部的操作
    lazy var reports: ()->String = {
        [weak self] in //[weak self] in 和 [unowned self] in作用一樣,區別見前面的weak和unowned的解釋
        if self!.temperature != nil {
            return "\(self!.location)的天氣預報是: \(self!.weather), 氣溫是\(self!.temperature)"
        } else {
            return "\(self!.location)的天氣預報是:\(self!.weather)"
        }
    }
    
    init(location: String, weather: String) {
        self.location = location
        self.weather = weather
    }
    
    deinit {
        debugPrint("\(location)的天氣預報實例被銷燬!")
    }
    
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章