最近在看Swift教程中的ARC部分,看到“解決實(shí)例間的強(qiáng)引用環(huán)”時(shí)镇匀,有些疑問(wèn),故做此記錄痊焊。
我們知道Swift采用ARC做內(nèi)存回收郑兴,高效的同時(shí)也會(huì)出現(xiàn)內(nèi)存泄漏的問(wèn)題犀斋,即實(shí)例間強(qiáng)引用。教程里給出了三種場(chǎng)景并給出了示例代碼情连。前兩種比較好理解叽粹,看的時(shí)候,我對(duì)第三種有些疑惑却舀。該場(chǎng)景是這樣的:兩個(gè)屬性都必須有值虫几,且初始化完成后不能為nil。這種場(chǎng)景下挽拔,則要一個(gè)類用無(wú)主引用屬性辆脸,另一個(gè)類用 隱式展開的可選屬性。我的疑惑是為什么要用隱式展開的可選屬性螃诅,為什么不能用普通類型的常量呢啡氢?OK,先看教程中的例子:
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
}
}
也就是术裸,var capitalCity: City!
能不能用 let capitalCity: City
代替倘是?看起來(lái)是可以的,挺滿足要求的:初始化后不能為nil袭艺。但是如果用 let capitalCity: City
會(huì)不滿足Swift的 兩段式構(gòu)造過(guò)程 :
階段1:
某個(gè)指定構(gòu)造器或便利構(gòu)造器被調(diào)用搀崭;
完成新實(shí)例內(nèi)存的分配,但此時(shí)內(nèi)存還沒(méi)有被初始化猾编;
指定構(gòu)造器確保其所在類引入的所有存儲(chǔ)型屬性都已賦初值瘤睹。存儲(chǔ)型屬性所屬的內(nèi)存完成初始化;
指定構(gòu)造器將調(diào)用父類的構(gòu)造器答倡,完成父類屬性的初始化轰传;
這個(gè)調(diào)用父類構(gòu)造器的過(guò)程沿著構(gòu)造器鏈一直往上執(zhí)行,直到到達(dá)構(gòu)造器鏈的最頂部瘪撇;
當(dāng)?shù)竭_(dá)了構(gòu)造器鏈最頂部获茬,且已確保所有實(shí)例包含的存儲(chǔ)型屬性都已經(jīng)賦值,這個(gè)實(shí)例的內(nèi)存被認(rèn)為已經(jīng)完全初始化设江。此時(shí)階段1完成。
階段2:
從頂部構(gòu)造器鏈一直往下攘轩,每個(gè)構(gòu)造器鏈中類的指定構(gòu)造器都有機(jī)會(huì)進(jìn)一步定制實(shí)例叉存。構(gòu)造器此時(shí)可以訪問(wèn)self、修改它的屬性并調(diào)用實(shí)例方法等等度帮。
最終歼捏,任意構(gòu)造器鏈中的便利構(gòu)造器可以有機(jī)會(huì)定制實(shí)例和使用self稿存。
即不滿足階段二:訪問(wèn)self時(shí),類成員變量必須初始化瞳秽。想要初始化成員變量必須調(diào)用 City
的初始化方法瓣履,此時(shí)需要訪問(wèn)self,但是此時(shí)成員變量 capitalCity
卻沒(méi)有初始化练俐,根據(jù)初始化過(guò)程規(guī)則此時(shí)不能訪問(wèn)self袖迎,這就造成了矛盾。而使用var capitalCity: City!
不會(huì)出現(xiàn)問(wèn)題腺晾,因?yàn)槠淠J(rèn)值為nil燕锥。在調(diào)用 City
初始化方式的時(shí)候,capitalCity
已經(jīng)初始化過(guò)了悯蝉,其值彼時(shí)為nil归形。
當(dāng)然你可以自己試下,改成 let capitalCity: City
后鼻由,編譯器會(huì)報(bào)如下錯(cuò)誤:
Playground execution failed: error: Constructor.xcplaygroundpage:30:28: error: 'self' used before all stored properties are initialized
self.capitalCity = City(name: capitalName, country: self)
^
Constructor.xcplaygroundpage:27:9: note: 'self.capitalCity' not initialized
var capitalCity: City
^
error: Constructor.xcplaygroundpage:30:61: error: variable 'self.capitalCity' used before being initialized
self.capitalCity = City(name: capitalName, country: self)
^