初始化器
1.類、結(jié)構(gòu)體、枚舉都可以定義初始化器
2.類有兩種初始化器:指定初始化器(Designated Initializer)、便捷初始化器(Convenience Initializer)
//指定初始化器
init(paramenters) {
statements
}
//便捷初始化器
convenience init(paramenters) {
statements
}
3.每個類至少有一個指定初始化器,指定初始化器是類的主要初始化器
4.默認初始化器總是類的指定初始化器
5.類偏向于少量定制初始化器,一個類通常只有一個指定初始化器
-
初始化器的相互調(diào)用規(guī)則
- 指定初始化器必須從它的直系父類調(diào)用指定初始化器
- 便捷初始化器必須從相同的類里調(diào)用另一個初始化器
- 便捷初始化器最終必須調(diào)用一個指定初始化器
- 確保使用任意的初始化器,都可以完整的初始化實例
兩段式初始化
在Swift編碼安全方面,為了保證初始化過程的安全,設定了兩段式初始化、安全檢查
-
兩段式初始化
- 第一階段:初始化所有存儲屬性
- 外層調(diào)用 指定/便捷初始化器
- 分配內(nèi)存給實例,但未初始化
- 指定初始化器確保當前類定義的存儲屬性都初始化
- 指定初始化器調(diào)用父類的初始化器,不斷向上調(diào)用,形成初始化器鏈
- 第二階段:設置新的存儲屬性值
- 從頂部初始化器向下,鏈中的每一個指定初始化器都有機會進一步制定實例
- 初始化器現(xiàn)在能夠使用
self(訪問、修改它的屬性,調(diào)用它的實例方法等) - 最終,鏈中任何便捷初始化器都有機會制定實例以及使用
self
- 第一階段:初始化所有存儲屬性
-
安全檢查
- 指定初始化器必須保證在調(diào)用父類初始化器之前,其所在類定義的所有存儲屬性都要初始化完成(完成賦值);
- 指定初始化器必須先調(diào)用父類初始化器,然后才能為繼承的屬性設置新的值;
- 便捷初始化器必須先調(diào)用同類中的其他初始化器,然后再為任意屬性設置新的值;
- 初始化器在第一階段初始化完成之前,不能調(diào)用任何實例方法、不能讀取任何實例屬性的值、也不能引用
self; - 直到第一階段結(jié)束,實例才算完全合法。
重寫初始化器
1.當重寫父類的指定初始化器時,必須加上override(即使子類的實現(xiàn)是便捷初始化器)
- 父類的指定初始化器,被重寫為指定初始化器
重寫的指定初始化器必須要調(diào)用父類的指定初始化器,還得先初始化當前類的存儲屬性;
override init(age: Int) {
self.score = 0
super.init(age: age)
}
- 父類的指定初始化器,可以被重寫為便捷初始化器
override convenience init(age: Int) {
super.init(age: age)
}
2.如果子類寫了一個匹配父類便捷出初始化器的初始化器,不用加上override
- 因為父類的便捷初始化器永遠不會通過子類直接調(diào)用
- 子類無法重寫父類的便捷初始化器。
自動繼承
1.如果子類沒有自定義任何指定初始化器,它會自動繼承父類所有的指定初始化器
class Person {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
init(age: Int) {
self.age = age
self.name = "Jack"
}
}
class Student: Person {
}
var s1 = Student(age: 10)
var s2 = Student(age: 10, name: "Rose")
2.如果子類提供了父類所有指定初始化器的實現(xiàn)(要么通過方式1繼承,要么重寫);子類自動繼承所有的父類便捷初始化器。
3.就算子類添加了更多的便捷初始化器,這些規(guī)則仍然適用
4.子類以便捷初始化器的形式重寫父類的指定初始化器,也可以作為滿足規(guī)則2的一部分
必要構(gòu)造器(required)
1.用required修飾指定初始化器,表明其子類都必須實現(xiàn)該初始化器(通過繼承或者重寫實現(xiàn))
class SomeClass {
required init() {
// 構(gòu)造器的實現(xiàn)代碼
}
}
2.如果子類重寫了required初始化器,也必須加required,不用加override
class SomeSubclass: SomeClass {
required init() {
// 構(gòu)造器的實現(xiàn)代碼
? }
}
屬性觀察器
父類的屬性在它自己的初始化器中賦值不會觸發(fā)屬性觀察器,但在子類的初始化器中賦值會觸發(fā)屬性觀察器
class Person {
var age: Int {
willSet {
print("Person willSet",newValue)
}
didSet {
print("Person didSet",oldValue,age)
}
}
init() {
self.age = 0
}
}
class Student: Person {
override init() {
super.init()
self.age = 10
}
}
var s1 = Student()
//Person willSet 10
//Person didSet 0 10
可失敗始化器
什么是可失敗初始化器?
- 調(diào)用這個初始化方法返回的對象可能是空(可選項)
- init?代表可失敗初始化器
1.類、結(jié)構(gòu)體、枚舉都可以使用init?定義可失敗初始化器
class Person {
var name: String
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
var p1 = Person(name: "") // nil
var p2 = Person(name: "Jack") //Jack
- String轉(zhuǎn)換Int,也是可失敗初始化器
var num = Int("123")
public init?(_ description: String)
- 枚舉的原始值
enum Answer: Int {
case wrong, right
}
//var answer: Answer?
var answer = Answer(rawValue: 0)
枚舉一旦有原始值,是允許使用傳rawValue的初始化器;通過rawValue創(chuàng)建枚舉實例的初始化器是可失敗初始化器的。
2.不允許同時定義參數(shù)標簽、參數(shù)個數(shù)、參數(shù)類型相同的可失敗初始化器和非可失敗初始化器
class Person {
var name: String
//可失敗初始化器
init?(name: String) {
if name .isEmpty {
return nil
}
self.name = name
}
//非可失敗初始化器
init(name: String) {
self.name = name
}
}
編譯器報錯:重復定義Invalid redeclaration of 'init(name:)'
3.可失敗初始化器可以調(diào)用非可失敗初始化器,非可失敗初始化器調(diào)用可失敗初始化器需要進行解包
- init? 調(diào)用 init
class Person {
var name: String
//可失敗初始化器(便捷初始化器)
convenience init?(name: String) {
self.init()
if name .isEmpty {
return nil
}
}
//非可失敗初始化器
init() {
self.name = ""
}
}
- init 調(diào)用 init?
class Person {
var name: String
//可失敗初始化器(便捷初始化器)
init?(name: String) {
if name .isEmpty {
return nil
}
self.name = name
}
//非可失敗初始化器
convenience init() {
self.init(name: "")
}
}
編譯器報錯:A non-failable initializer cannot delegate to failable initializer 'init(name:)' written with 'init?'
原因:非可失敗初始化器只能接受正常的Person對象,不能出現(xiàn)有空的情況,
self.init(name: "")可失敗初始化器可能會出現(xiàn)空的。
因此,只需要在后面加個感嘆號!進行解包。
self.init(name: "")!
4.可以用init!定義隱式解包的可失敗初始化器
class Person {
var name: String
//可失敗初始化器(便捷初始化器)
init!(name: String) {
if name .isEmpty {
return nil
}
self.name = name
}
//非可失敗初始化器
convenience init() {
self.init(name: "")
}
}
??這樣寫會有問題,如果非可失敗初始化器self.init(name: "")傳過去的是空字符串,空字符串到可失敗初始化器就會返回nil,返回nil在非可失敗初始化器進行解包就會導致程序崩潰。
5.如果初始化器調(diào)用一個可失敗初始化器導致初始化失敗,那么整個初始化過程都失敗,并且之后的代碼都停止執(zhí)行
-
init? 調(diào)用 init?
class Person { var name: String //可失敗初始化器 init?(name: String) { if name .isEmpty { return nil } self.name = name } //可失敗初始化器 convenience init?() { self.init(name: "") self.name = "Jack" print(".......") } } var person = Person()
6.可以用一個非可失敗初始化器重寫一個可失敗初始化器,但反過來是不行的。
- 非可失敗初始化器可以重寫可失敗初始化器
class Student: Person {
override init?(name: String) {
super.init(name: name)
}
}
- 可失敗初始化器不可以重寫非可失敗初始化器
class Student: Person {
override init?(name: String) {
super.init(name: name)
}
}
編譯器報錯:Failable initializer 'init(name:)' cannot override a non-failable initializer
反始化器(deinit)
- deinit叫做反初始化器,類似于C++de析構(gòu)器、Objective-C的dealloc
- 當類的實例對象被釋放內(nèi)存時,就會調(diào)用實例對象的deinit方法
class Person {
deinit {
print("Person deinit")
}
}
class Student: Person {
deinit {
print("Person deinit")
}
}