Swift 提供了兩種辦法用來(lái)解決你在使用類的屬性時(shí)所遇到的循環(huán)強(qiáng)引用問(wèn)題:弱引用(weak reference)和無(wú)主引用(unowned reference)。
弱引用和無(wú)主引用允許循環(huán)引用中的一個(gè)實(shí)例引用另外一個(gè)實(shí)例而不保持強(qiáng)引用。這樣實(shí)例能夠互相引用而不產(chǎn)生循環(huán)強(qiáng)引用。
對(duì)于生命周期中會(huì)變?yōu)?code style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; font-size: 14px; padding: 0px 5px; color: rgb(199, 37, 78); background-color: rgb(248, 248, 248); white-space: nowrap; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; margin: 0px 2px; border: 1px solid rgb(234, 234, 234);">nil的實(shí)例使用弱引用。相反的,對(duì)于初始化賦值后再也不會(huì)被賦值為nil
的實(shí)例,使用無(wú)主引用。
弱引用不會(huì)牢牢保持住引用的實(shí)例,并且不會(huì)阻止 ARC 銷毀被引用的實(shí)例。這種行為阻止了引用變?yōu)檠h(huán)強(qiáng)引用。聲明屬性或者變量時(shí),在前面加上weak
關(guān)鍵字表明這是一個(gè)弱引用。
在實(shí)例的生命周期中,如果某些時(shí)候引用沒(méi)有值,那么弱引用可以阻止循環(huán)強(qiáng)引用。如果引用總是有值,則可以使用無(wú)主引用,在無(wú)主引用中有描述。在上面Apartment
的例子中,一個(gè)公寓的生命周期中,有時(shí)是沒(méi)有“居民”的,因此適合使用弱引用來(lái)解決循環(huán)強(qiáng)引用。
注意:
弱引用必須被聲明為變量,表明其值能在運(yùn)行時(shí)被修改。弱引用不能被聲明為常量。
因?yàn)槿跻每梢詻](méi)有值,你必須將每一個(gè)弱引用聲明為可選類型。可選類型是在 Swift 語(yǔ)言中推薦的用來(lái)表示可能沒(méi)有值的類型。
因?yàn)槿跻貌粫?huì)保持所引用的實(shí)例,即使引用存在,實(shí)例也有可能被銷毀。因此,ARC 會(huì)在引用的實(shí)例被銷毀后自動(dòng)將其賦值為nil
。你可以像其他可選值一樣,檢查弱引用的值是否存在,你永遠(yuǎn)也不會(huì)遇到被銷毀了而不存在的實(shí)例。
下面的例子跟上面Person
和Apartment
的例子一致,但是有一個(gè)重要的區(qū)別。這一次,Apartment
的tenant
屬性被聲明為弱引用:
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 }
weak var tenant: Person?
deinit { println("Apartment #\(number) is being deinitialized") }
}
然后跟之前一樣,建立兩個(gè)變量(john和number73)之間的強(qiáng)引用,并關(guān)聯(lián)兩個(gè)實(shí)例:
var john: Person?
var number73: Apartment?
john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)
john!.apartment = number73
number73!.tenant = john
現(xiàn)在,兩個(gè)關(guān)聯(lián)在一起的實(shí)例的引用關(guān)系如下圖所示:
Person
實(shí)例依然保持對(duì)Apartment
實(shí)例的強(qiáng)引用,但是Apartment
實(shí)例只是對(duì)Person
實(shí)例的弱引用。這意味著當(dāng)你斷開john
變量所保持的強(qiáng)引用時(shí),再也沒(méi)有指向Person
實(shí)例的強(qiáng)引用了:
由于再也沒(méi)有指向Person
實(shí)例的強(qiáng)引用,該實(shí)例會(huì)被銷毀:
john = nil
// prints "John Appleseed is being deinitialized"
唯一剩下的指向Apartment
實(shí)例的強(qiáng)引用來(lái)自于變量number73
。如果你斷開這個(gè)強(qiáng)引用,再也沒(méi)有指向Apartment
實(shí)例的強(qiáng)引用了:
由于再也沒(méi)有指向Apartment
實(shí)例的強(qiáng)引用,該實(shí)例也會(huì)被銷毀:
number73 = nil
// prints "Apartment #73 is being deinitialized"
上面的兩段代碼展示了變量john
和number73
在被賦值為nil
后,Person
實(shí)例和Apartment
實(shí)例的析構(gòu)函數(shù)都打印出“銷毀”的信息。這證明了引用循環(huán)被打破了。
和弱引用類似,無(wú)主引用不會(huì)牢牢保持住引用的實(shí)例。和弱引用不同的是,無(wú)主引用是永遠(yuǎn)有值的。因此,無(wú)主引用總是被定義為非可選類型(non-optional type)。你可以在聲明屬性或者變量時(shí),在前面加上關(guān)鍵字unowned
表示這是一個(gè)無(wú)主引用。
由于無(wú)主引用是非可選類型,你不需要在使用它的時(shí)候?qū)⑺归_。無(wú)主引用總是可以被直接訪問(wèn)。不過(guò) ARC 無(wú)法在實(shí)例被銷毀后將無(wú)主引用設(shè)為nil
,因?yàn)榉强蛇x類型的變量不允許被賦值為nil
。
注意:
如果你試圖在實(shí)例被銷毀后,訪問(wèn)該實(shí)例的無(wú)主引用,會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。使用無(wú)主引用,你必須確保引用始終指向一個(gè)未銷毀的實(shí)例。
還需要注意的是如果你試圖訪問(wèn)實(shí)例已經(jīng)被銷毀的無(wú)主引用,程序會(huì)直接崩潰,而不會(huì)發(fā)生無(wú)法預(yù)期的行為。所以你應(yīng)當(dāng)避免這樣的事情發(fā)生。
下面的例子定義了兩個(gè)類,上一篇:Swift變量聲明下一篇:Swift Any和AnyObject類型轉(zhuǎn)換