Swift知識點23 - 自動引用計數

引用計數僅僅應用于類的實例。結構體和枚舉類型是值類型,不是引用類型,也不是通過引用的方式存儲和傳遞。

無論你將實例賦值給屬性、常量或變量,它們都會創建此實例的強引用。之所以稱之為“強”引用,是因為它會將實例牢牢地保持住,只要強引用還在,實例是不允許被銷毀的。

如果兩個類實例互相持有對方的強引用,因而每個實例都讓對方一直存在,就是這種情況。這就是所謂的循環強引用。

你可以通過定義類之間的關系為弱引用或無主引用,以替代強引用,從而解決循環強引用的問題。

使用類屬性遇到的強引用解決辦法:

  1. 弱引用(weak reference)
  2. 無主引用(unowned reference)

什么時候使用weak和unowned?
unowned:同時銷毀
weak:被捕獲的self有可能比閉包銷毀早

/*
 弱引用:被捕獲的self比其他實例早銷毀的話,用weak修飾
 無主引用:被捕獲的self和其他實例同時銷毀或者更晚的聲明周期的話,用onwned修飾
 */

情況:self銷毀了,閉包沒有銷毀,如果使用unowned的話,會出現崩潰,使用weak則沒有問題。

當其他的實例有更短的生命周期時,使用弱引用。
當其他實例有相同的或者更長生命周期時,請使用無主引用。

無主引用:
無主引用在其他實例有相同或者更長的生命周期時使用。你可以在聲明屬性或者變量時,在前面加上關鍵字 unowned 表示這是一個無主引用。

和OC一樣,Swift也是用ARC,也會有循環引用導致內存泄露
如果屬性是可選類型,只能用weak修飾符避免循環引用。所引用對象被回收后改屬性會被自動置為nil
如果屬性不是可選類型,只能用無主引用(unowned)。所引用對象被回收后屬性不會被置為nil,此時訪問會導致運行時錯誤。類似OC中的unsafe_unretained修飾符。

Person 和 Apartment 的例子展示了兩個屬性的值都允許為 nil,并會潛在的產生循環強引用。這種場景最適合用弱引用來解決。

Customer 和 CreditCard 的例子展示了一個屬性的值允許為 nil,而另一個屬性的值不允許為 nil,這也可能會產生循環強引用。這種場景最適合通過無主引用來解決。

無主引用和隱式解析可選屬性

在這種場景中,兩個屬性都必須有值,并且初始化完成后永遠不會為 nil。在這種場景中,需要一個類使用無主屬性,而另外一個類使用隱式解析可選屬性。

class Country {
    let name: String
    var capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

閉包的循環強引用

將一個閉包賦值個類實例的某一個屬性,而這個閉包又使用了這個類實例時,使用了self,導致閉包捕獲了self,引起強引用。

循環強引用的產生,是因為閉包和類一樣,都是引用類型。

解決閉包的循環強引用:
聲明每個捕獲的引用為弱引用或無主引用,根據代碼關系來決定使用弱引用還是無主引用。

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

定義捕獲列表

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

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

在閉包和捕獲的實例總是互相引用并且總是同時銷毀時,將閉包內的捕獲定義為 無主引用。

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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容