位置:首頁 > 高級語言 > Swift教學 > Swift類實例之間的循環強引用

Swift類實例之間的循環強引用

類實例之間的循環強引用

在上麵的例子中,ARC 會跟蹤你所新創建的Person實例的引用數量,並且會在Person實例不再被需要時銷毀它。

然而,我們可能會寫出這樣的代碼,一個類永遠不會有0個強引用。這種情況發生在兩個類實例互相保持對方的強引用,並讓對方不被銷毀。這就是所謂的循環強引用。

你可以通過定義類之間的關係為弱引用或者無主引用,以此替代強引用,從而解決循環強引用的問題。具體的過程在解決類實例之間的循環強引用中有描述。不管怎樣,在你學習怎樣解決循環強引用之前,很有必要了解一下它是怎樣產生的。

下麵展示了一個不經意產生循環強引用的例子。例子定義了兩個類:PersonApartment,用來建模公寓和它其中的居民:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { println("\(name) is being deinitialized") }
}
class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { println("Apartment #\(number) is being deinitialized") }
}

每一個Person實例有一個類型為String,名字為name的屬性,並有一個可選的初始化為nilapartment屬性。apartment屬性是可選的,因為一個人並不總是擁有公寓。

類似的,每個Apartment實例有一個叫number,類型為Int的屬性,並有一個可選的初始化為niltenant屬性。tenant屬性是可選的,因為一棟公寓並不總是有居民。

這兩個類都定義了析構函數,用以在類實例被析構的時候輸出信息。這讓你能夠知曉PersonApartment的實例是否像預期的那樣被銷毀。

接下來的代碼片段定義了兩個可選類型的變量johnnumber73,並分彆被設定為下麵的ApartmentPerson的實例。這兩個變量都被初始化為nil,並為可選的:

var john: Person?
var number73: Apartment?

現在你可以創建特定的PersonApartment實例並將類實例賦值給johnnumber73變量:

john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)

在兩個實例被創建和賦值後,下圖表現了強引用的關係。變量john現在有一個指向Person實例的強引用,而變量number73有一個指向Apartment實例的強引用:

現在你能夠將這兩個實例關聯在一起,這樣人就能有公寓住了,而公寓也有了房客。注意感歎號是用來展開和訪問可選變量johnnumber73中的實例,這樣實例的屬性才能被賦值:

john!.apartment = number73
number73!.tenant = john

在將兩個實例聯係在一起之後,強引用的關係如圖所示:

不幸的是,將這兩個實例關聯在一起之後,一個循環強引用被創建了。Person實例現在有了一個指向Apartment實例的強引用,而Apartment實例也有了一個指向Person實例的強引用。因此,當你斷開johnnumber73變量所持有的強引用時,引用計數並不會降為 0,實例也不會被 ARC 銷毀:

john = nil
number73 = nil

注意,當你把這兩個變量設為nil時,冇有任何一個析構函數被調用。強引用循環阻止了PersonApartment類實例的銷毀,並在你的應用程序中造成了內存泄漏。

在你將johnnumber73賦值為nil後,強引用關係如下圖:

PersonApartment實例之間的強引用關係保留了下來並且不會被斷開。