引用計數僅僅應用于類的實例。結構體和枚舉類型是值類型,不是引用類型,也不是通過引用的方式存儲和傳遞。
無論你將實例賦值給屬性、常量或變量,它們都會創建此實例的強引用。之所以稱之為“強”引用,是因為它會將實例牢牢地保持住,只要強引用還在,實例是不允許被銷毀的。
如果兩個類實例互相持有對方的強引用,因而每個實例都讓對方一直存在,就是這種情況。這就是所謂的循環強引用。
你可以通過定義類之間的關系為弱引用或無主引用,以替代強引用,從而解決循環強引用的問題。
使用類屬性遇到的強引用解決辦法:
- 弱引用(weak reference)
- 無主引用(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,應該用無主引用,而不是弱引用。