- 所有類的存儲屬性(包括從它的父類繼承的所有屬性)都必須在初始化期間分配初始值
-
Swift
為類類型定義了兩種初始化器以確保所有的存儲屬性接收一個初始值. 這些就是所謂的指定初始化器和便捷初始化器 - 指定初始化器是類的主要初始化器. 指定的初始化器可以初始化所有那個類引用的屬性并且條用合適的父類初始化器來繼續(xù)這個初始化過程給父類鏈
- 類偏向于少量初始化器, 并且一個類通常只有一個指定初始化器. 指定初始化器是初始化開始并持續(xù)初始化過程到父類鏈的"傳送"點(diǎn)
- 每個類至少得有一個指定初始化器. 如同在初始化器的自動繼承里描述的那樣, 在某些情況下, 這些需求通過從父類繼承一個或多個指定初始化器來滿足.
- 便捷初始化器
(convenience)
是次要的. 你可以在相同的類里定義一個便捷初始化器來調(diào)用一個指定的初始化器作為便捷初始化器來給指定初始化器設(shè)置默認(rèn)形式參數(shù). 你也可以為具體的使用情況或輸入的值類型定義一個便捷初始化器從而創(chuàng)建這個類的實(shí)例. - 如果你的類不需要便捷初始化器你可以不提供它. 在為通用的初始化模式創(chuàng)建快捷方式以節(jié)省時間或者類的初始化更加清晰明了的時候使用便捷初始化器
注: 最開始接觸Swift 2.x的時候, 那會我對便捷初始化的稱呼是: 便利構(gòu)造函數(shù), 其實(shí)一個意思
指定初始化器和便捷初始化器
- 用于值類型的簡單初始化器相同的方式來寫類的指定初始化器
- 用
convenience
修飾符放在init
關(guān)鍵字前定義便捷初始化器
指定初始化器語法
init(parameters) {
statements
}
便捷初始化器語法
convenience init(parameters) {
statements
}
類的初始化委托
- 指定初始化器必須從它的直系父類調(diào)用指定初始化器
- 便捷初始化器必須從相同的類里調(diào)用另一個初始化器
- 便捷初始化器最終必須調(diào)用一個指定初始化器
類的初始化委托.png
兩段式初始化
-
Swift
的類初始化是一個兩段式過程. 在第一個階段, 每一個存儲屬性被引入類分配了一個初始值. 一旦每個存儲屬性的初始狀態(tài)被確定, 第二個階段就開始了, 每個類都有機(jī)會在新的實(shí)例準(zhǔn)備使用之前來定制它的存儲屬性
- 指定或便捷初始化器在類中被調(diào)用
- 為這個類的新實(shí)例分配內(nèi)存. 內(nèi)存還沒有被初始化
- 這個類的指定初始化器確保所有由此類引入的存儲屬性都有一個值. 現(xiàn)在這些存儲屬性的內(nèi)存被初始化
- 指定初始化器上交父類的初始化器為其存儲屬性執(zhí)行相同的任務(wù)
- 這個調(diào)用父類初始化器的過程將沿著初始化器鏈一直向上進(jìn)行, 知道到達(dá)初始化器鏈的最頂部
- 一旦到達(dá)了初始化器鏈的最頂部, 在鏈頂部的類確保所有的存儲屬性都有一個值, 此實(shí)例的內(nèi)存被認(rèn)為完全初始化了, 此時第一階段完成
- 從頂部初始化器往下, 鏈中的每一個指定初始化器都有機(jī)會進(jìn)一步定制示例. 初始化器現(xiàn)在能夠訪問
self
并且可以修改它的屬性, 調(diào)用它的實(shí)例方法等等- 最終, 鏈中任何便捷初始化器都有機(jī)會定制實(shí)例以及使用
self
, 此時第二階段完成
- 兩段式初始化過程的使用讓初始化更加安全, 同時在每個類的層級結(jié)構(gòu)給與了完備的靈活性. 兩段式初始化過程可以防止屬性值在初始化期間被訪問, 還可以防止屬性值被另一個初始化器意外的賦予不同的值.
安全檢查
- 指定初始化器必須保證在向上委托給父類初始化器之前, 其所在類引入的所有屬性都要初始化完成.
- 指定初始化器必須向上委托父類初始化器, 然后才能為繼承的屬性設(shè)置新值. 如果不這樣做, 指定初始化器富裕的新值將被父類中的初始化器鎖覆蓋.
- 便捷初始化器必須先委托同類中的其它初始化器, 然后再為任意屬性賦新值(包括同類里定義的屬性). 如果沒這么做, 便捷初始化器賦予的新值將被自己類中其它指定初始化器所覆蓋.
- 初始化器在第一階段初始化完成之前, 不能調(diào)用任何實(shí)例方法、不能讀取任何實(shí)例屬性的值, 也不能引用
self
作為值.
e.g. 安全檢查
- 錯誤1
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
convenience init() {
self.init(name: "unKonw", age: 0)
}
}
class Teacher: Person {
var salary: Int
init(name: String, age: Int, salary: Int) {
super.init(name: name, age: age)
self.salary = salary
self.name = name + "老師"
}
convenience init(name: String) {
self.init(name: name, age: 30, salary: 5000)
}
func showInfo() -> Void {
print("name: \(name) age: \(age) salary: \(salary)")
}
}
這個示例違背了第1條
安全檢查, 錯誤代碼:
error1.png
在Teacher
類中,salary
是該類下新增屬性, 在調(diào)用super
初始化器前, 并未完成salary
屬性的初始化
所以需要將salary
屬性初始化放在調(diào)用super
之前正確的代碼:
init(name: String, age: Int, salary: Int) {
self.salary = salary
super.init(name: name, age: age)
}
- 錯誤2:
error2.png
其余代碼部分一致, 只是將name
屬性初始化的位置進(jìn)行了調(diào)整,name
是Teacher
父類的屬性, 這種寫法違背了安全檢查第2條
, 父類的屬性需要在調(diào)用父類的初始化器后才能賦予新值, 所以正確寫法是:
init(name: String, age: Int, salary: Int) {
self.salary = salary
super.init(name: name, age: age)
self.name = name + "老師"
}
- 錯誤3:
error3.png
在Teacher
的便捷初始化器下賦予name
屬性新值, 因?yàn)?code>name是父類屬性, 同樣需要在調(diào)用父類的初始化器后才能賦予新值, 而又因?yàn)槭窃诋?dāng)前類的便利初始化器中, 需要通過調(diào)用自身的指定初始化器完成初始化
所以正確的寫法是:
convenience init(name: String) {
self.init(name: name, age: 30, salary: 5000)
self.name = name + "老師"
}
- 錯誤4:
error4.png
第4條
, 在初始化完成前調(diào)用了實(shí)例方法
正確方式:
init(name: String, age: Int, salary: Int) {
self.salary = salary
super.init(name: name, age: age)
self.name = name + "老師"
self.showInfo()
}
初始化器的繼承和重寫
- 不像在
Object-C
中的子類,Swift
的子類不會默認(rèn)繼承父類的初始化器.Swift
的這種機(jī)制防止父類的簡單初始化器被一個更專用的子類繼承并被用來創(chuàng)建一個沒有完全或錯誤初始化的新實(shí)例的情況發(fā)生.只有在特定情況下才會繼承父類的初始化器
- 如果你想自定義的子類來實(shí)現(xiàn)一個或多個和父類相同的初始化器, 你可以在子類中為那些初始化器提供定制的實(shí)現(xiàn).
- 當(dāng)你寫的子類初始化器匹配父類指定初始化器的時候, 你實(shí)際上可以重寫那個初始化器. 因此, 在子類的初始化器定義之前你必須寫
override
修飾符. 如同默認(rèn)初始化器所描述的那樣, 即使是自動提供的默認(rèn)初始化器你也可以重寫.
初始化器的自動繼承
- 如果你的子類沒有定義任何指定初始化器, 他會自動繼承父類所有的指定初始化器.
- 如果你的子類提供了所有父類指定初始化器的實(shí)現(xiàn)--要么是通過規(guī)則1繼承來的, 要么通過在定義中提供自定義實(shí)現(xiàn)的--那么它自動繼承所有的父類便捷初始化器.
可失敗初始化器
- 定義類艾君、結(jié)構(gòu)體或枚舉初始化時可以失敗, 在某些情況下會管大用.這個失敗可能由以下幾種方式觸發(fā), 包括給初始化傳入無效的形式參數(shù)值, 或缺少某種外部所需的資源, 又或是其他阻止初始化的情況.
- 為了妥善處理這種可能失敗的情況, 在類梳毙、結(jié)構(gòu)體或枚舉中定義一個或多個可失敗的初始化器. 通過在
init
關(guān)鍵字后面添加問號init?
來實(shí)現(xiàn)
必要初始化器
- 在類的初始化器前添加
required
修飾符來表明所有該類的子類都必須實(shí)現(xiàn)該初始化器.
反初始化
- 在類實(shí)例被釋放的時候, 反初始化器就會立即被調(diào)用. 你可以使用
deinit
關(guān)鍵字來寫反初始化器, 就如同寫初始化器用init
關(guān)鍵字一樣. 反初始化器只在類類型中有效 - 反初始化器會在實(shí)例被釋放之前自動被調(diào)用. 你不能自定調(diào)用反初始化器. 父類的反初始化器可以被子類繼承, 并且子類的反初始化器實(shí)現(xiàn)結(jié)束之后父類的反初始化器會被調(diào)用. 父類的反初始化器總會被調(diào)用, 就算子類沒有反初始化器.
- 每個類當(dāng)中只能有一個反初始化器. 反初始化器不接收任何形式參數(shù), 并且不需要寫圓括號.
deinit {
}