在swift中,對象的初始化和oc中類似,分為兩個階段爆办,一個階段是對象的內(nèi)存分配,另一個階段是對對象中的store value進(jìn)行初始化课梳。不過第一個階段和oc略有不同距辆,oc在第一階段分配內(nèi)存后余佃,會將所有的屬性設(shè)為0或者null,而swift則更為靈活跨算,可以指定一些除0以外的值爆土。
全能初始化方法和便利初始化方法
在詳細(xì)分析兩段式(two-phrase)初始化之前,先了解一下swift中兩種初始化方法:全能初始化方法(designated initializier)和便利初始化方法(convenience initializer)诸蚕。
全能初始化方法
就像其名字一樣步势,通過全能初始化方法來初始化一個對象后,其所有的store property都能夠進(jìn)行初始化背犯。一個類可以有多個全能初始化方法坏瘩,但是一般來說只有一個。只要保證全能初始化方法能夠初始化所有屬性即可漠魏。
class GameCharacter {
var weapon: String
init() {
self.weapon = "fist"
}
init(weapon: String) {
self.weapon = weapon
}
}
如上倔矾,Person類有兩個全能初始化方法,一個init()
柱锹,一個是init(name: String)
哪自,每個初始化方法都能夠?qū)ame進(jìn)行初始化。
如果不在初始化方法中對非可選值的屬性進(jìn)行初始化禁熏,編譯器會提示未初始化的錯誤提陶。
便利初始化方法
便利初始化方法則只是初始化類中部分屬性∑ゲ悖看以下代碼
class Warrior: GameCharacter {
var shield: String
init(weapon: String, shield: String) {
self.shield = shield
super.init(weapon: weapon)
}
convenience init(shield: String) {
self.init(weapon: "fist", shield: shield)
}
}
Warrior有一個全能初始化方法init(weapon: String, shield: String
隙笆,這個初始化方法可以初始化所有的屬性,也就是shield
和從父類繼承來的weapon
升筏。另外還有一個便利初始化方法init(shield: String)
撑柔,該方法只顯示指定了shield的值,而weapon的值則是提供一個默認(rèn)的值您访。我們注意到在這個初始化方法在前面加了一個關(guān)鍵字convenience
铅忿。這個convenience除了表明這個方法是便利初始化方法外,還有一個作用就是讓編譯器進(jìn)行一些錯誤檢查灵汪。
全能初始化方法和便利初始化方法之間的調(diào)用規(guī)則
全能初始化方法和便利初始化方法調(diào)用規(guī)則有三個
- 全能初始化方法必須調(diào)用父類的全能初始化方法
- 便利初始化方法只能調(diào)用當(dāng)前類的初始化方法檀训,不能調(diào)用父類的初始化方法
- 便利初始化方法,最終必須調(diào)用當(dāng)前類的全能初始化方法享言。
下圖可以很直觀的表明這三個規(guī)則:
類的兩段式初始化
swift和oc最大的區(qū)別峻凫,除了多了很多新的特性,比如新的枚舉類型览露,struct類型等荧琼,最明顯的區(qū)別就是在編譯器上面。swift的編譯器在編譯期間做了很多的安全檢查工作,比如對于一個可能為nil的值命锄,必須進(jìn)行解包堰乔;兩個不同類型的變量不能相互進(jìn)行賦值(即使是Double和Int之間的也不能隱式轉(zhuǎn)換)。類的初始化方法也一樣脐恩。
在類的初始化方法中镐侯,有四個安全性的檢查:
- 全能初始化方法必須確保在父類被初始化之前,所有的屬性被正確初始化
- 再給被繼承的屬性賦值之前驶冒,必須調(diào)用父類的全能初始化方法苟翻,也就是說,父類必須被初始化之后才能給被集成的屬性賦值
- 便利初始化方法在對屬性賦值之前只怎,必須調(diào)用其它初始化方法
- 在當(dāng)前類沒有被正確初始化之前(即第一階段),不能調(diào)用類的實例方法和讀取實例屬性
下面這段代碼說明了這四個檢查
class Warrior: GameCharacter {
var shield: String
init(weapon: String, shield: String) {
self.shield = shield // 在調(diào)用父類之前怜俐,必須對初始化本類的屬性
// attack() 編譯器錯誤身堡,當(dāng)前類還沒初始化完成,不能調(diào)用實例方法
super.init(weapon: weapon)
self.weapon = "sword" //在父類初始化完成后拍鲤,才能對從父類集成的屬性進(jìn)行賦值
}
convenience init(shield: String) {
self.init(weapon: "fist", shield: shield)
self.shield = "silver shield" //便利初始化方法在調(diào)用其它初始化方法后贴谎,才能對屬性進(jìn)行賦值
}
func attack() {
Swift.print("use \(weapon) to attack")
}
}
如果以上四個檢查任何一個不符合,編譯器都會報錯季稳。
而swift的兩段式初始化正式基于這四個安全檢查的擅这,下面來看看這兩個階段分別作了什么
第一階段
- 調(diào)用全能初始化方法或者便利初始化方法,系統(tǒng)分配內(nèi)存景鼠,但是并未對這個類進(jìn)行初始化
- 在全能初始化方法中初始化所有屬性
- 調(diào)用父類的全能初始化方法執(zhí)行前兩個相同的步驟
- 沿著繼承鏈不斷向上一直到根類為止
- 這個時候在繼承鏈上的類都被正確的初始化仲翎,第一階段完成
第二階段
- 從根類開始向下返回,每個類的全能初始化方法都能夠進(jìn)行更多的對類的初始化操作铛漓,包括可以使用屬性溯香,可以調(diào)用實例方法
- 最后,任何便利方法都有機會去對類進(jìn)行更多的初始化
沿用上一個代碼片段浓恶,當(dāng)調(diào)用init(weapon: String, shield: String)
時玫坛,第一階段的初始化就開始了,直到GameCharacter的全能初始化方法中的self.weapon = "fist"
包晰,這時候第一階段結(jié)束湿镀。然后再沿著繼承鏈向下,直到Warrior類的全能初始化方法中的self.weapon = "sword
時伐憾,第二階段結(jié)束勉痴。
我們可以看到,swift的二段式初始化能夠保證我們在使用一個類的時候能夠被正確的初始化树肃,而且這個過程都是在編譯時檢查的蚀腿,這樣就能夠確保我們在編寫類的初始化代碼的時候不會犯錯。