初始化器
- 類、結構體、枚舉都可以定義初始化器
- 類有2種初始化器:指定初始化器(designated initializer)、便捷初始化器(convenience initializer)
- 每個類至少有一個指定初始化器,指定初始化器是類的主要初始化器
- 默認初始化器總是類的指定初始化器 ,當你沒寫新的指定初始化器,會自帶默認初始化器,一旦自己寫了新的指定初始化器,默認初始化器就自動消失,不能再用了,但是當你只寫了一個新的便捷初始化器,默認初始化器依然存在,依然可用。
- 類偏向於少量指定初始化器,一個類通常只有一個指定初始化器
class Size {
var width: Int = 0
var height: Int = 0
//指定初始化器(主要初始化器)
init(width: Int, height: Int) {
self.width = width
self.height = height
}
convenience init(width: Int) {
self.init(width: width, height: 0)
}
}
var s2 = Size(width: 10, height: 20)
var s3 = Size(width: 10)
這樣就可以使得每一個屬性都有一個初始化值,保證安全
- 初始化器的相互調用規則
- 指定初始化器必須從它的直系父類調用指定初始化器
class Person {
var age: Int
init(age: Int) {
self.age = age
}
convenience init() {
self.init(age: 0)
}
}
class Student: Person {
var score: Int
init(age: Int, score: Int) {
self.score = score
super.init(age: age)
self.age = 10
}
}
由上圖可知,子類的指定初始化器一定要最終調用父類的指定初始化器,並且調用順序要寫在子類自定義的屬性後面,並且沒調用父類的指定初始化器前,不能使用從父類繼承過來的屬性。
2. 便捷初始化器必須從相同的類裏調用另一個初始化器,只寫一個便捷初始化器,會報錯
改成下面這樣,調用個指定初始化器就對了:
3. 便捷初始化器最終必須調用一個指定初始化器
注意:同一個類的指定初始化器不能相互調用,只有便捷初始化器可以調用指定初始化器,便捷初始化器可以互相調用,但是不能造成循環喲
初始化器的相互調用
簡單調用:
複雜調用:
- 這一套規則保證了:使用任意初始化器,都可以完整的初始化實例
兩段式初始化
Swift在編碼安全方面是煞費苦心,爲了保證初始化過程的安全,設定了兩段式初始化、 安全檢查
兩段式初始化
- 第1階段:初始化所有存儲屬性
- 外層調用指定\便捷初始化器
- 分配內存給實例,但未初始化
- 指定初始化器確保當前類定義的存儲屬性都初始化
- 指定初始化器調用父類的初始化器,不斷向上調用,形成初始化器鏈
- 在第一階段時,不能使用self
- 第2階段:設置新的存儲屬性值
- 從頂部初始化器往下,鏈中的每一個指定初始化器都有機會進一步定製實例
- 初始化器現在能夠使用self(訪問、修改它的屬性,調用它的實例方法等等)
- 最終,鏈中任何便捷初始化器都有機會定製實例以及使用self
安全檢查
- 指定初始化器必須保證在調用父類初始化器之前,其所在類定義的所有存儲屬性都要初始化完成
- 指定初始化器必須先調用父類初始化器,然後才能爲繼承的屬性設置新值
- 便捷初始化器必須先調用同類中的其它初始化器,然後再爲任意屬性設置新值
- 初始化器在第1階段初始化完成之前,不能調用任何實例方法、不能讀取任何實例屬性的值,也不能引用self
- 直到第1階段結束,實例纔算完全合法
重寫
當重寫父類的指定初始化器時,必須加上override(即使子類的實現是便捷初始化器)
class Person {
var age: Int
init(age: Int) {
self.age = age
}
}
class Student : Person {
var score: Int
init(age: Int, score: Int) {
self.score = score
super.init(age: age)
}
//1. 父類的指定初始化器可以重寫爲指定初始化器
// override init(age: Int) {
// self.score = 0
// super.init(age: age)
// }
//2.父類的指定初始化器也可以重寫爲便捷初始化器
override convenience init(age: Int) {
self.init(age: age, score: 0)
}
}
如果子類寫了一個匹配父類便捷初始化器的初始化器,不用加上override,因爲父類的便捷初始化器永遠不會通過子類直接調用,因此,嚴格來說,子類無法重寫父類的便捷初始化器
class Person {
var age: Int
init(age: Int) {
self.age = age
}
convenience init() {
self.init(age: 0)
}
}
class Student : Person {
var score: Int
init(age: Int, score: Int) {
self.score = score
super.init(age: age)
}
//雖然方法名字與父類的便捷初始化器相同,但是super調用的是指定初始化器,不是便捷初始化器,所以不能叫重寫
//1
init() {
self.score = 0
super.init(age: 0)
}
//2 也可以寫成這種形式,但是隻能叫匹配,不能叫重寫
// convenience init() {
// self.init(age: 0, score: 0)
// }
}
自動繼承
- 如果子類沒有自定義任何指定初始化器,它會自動繼承父類所有的指定初始化器
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 = ""
}
}
class Student : Person {
}
//我們可以看到,由於Student沒有自定義任何指定初始化器,所以它繼承了父類所有的指定初始化器
var stu1 = Student(age: 10)
var stu2 = Student(age: 20, name: "Jack")
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 = ""
}
}
class Student : Person {
convenience init() {
//由於沒有自定義任何父類的指定初始化器,只是定義了一個便捷初始化器,所以還是會繼承所有父類的指定初始化器,1和2兩種方式都可以用
//1
// self.init(age: 0)
//2
self.init(age: 0, name: "Jack")
}
}
var stu1 = Student()
2. 如果子類提供了父類所有指定初始化器的實現(要麼通過方式1繼承,要麼重寫),子類自動繼承所有的父類便捷初始化器
class Person {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
init() {
self.age = 0
self.name = ""
}
convenience init(age: Int) {
self.init(age: age, name: "")
}
convenience init(name: String) {
self.init(age: 0, name: name)
}
}
class Student : Person {
override init(age: Int, name: String) {
super.init(age: age, name: name)
}
override init() {
super.init(age: 0, name: "")
}
}
//子類提供了父類所有指定初始化器的實現, 就會自動繼承所有的父類便捷初始化器
var stu1 = Student(age: 0, name: "Jack")
var stu2 = Student(age: 0)
var stu3 = Student(name: "Jack")
var stu4 = Student()
3. 就算子類添加了更多的便捷初始化器,這些規則仍然適用
4. 子類以便捷初始化器的形式重寫父類的指定初始化器,也可以作爲滿足規則2的一部分
class Person {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
init() {
self.age = 0
self.name = ""
}
convenience init(age: Int) {
self.init(age: age, name: "")
}
convenience init(name: String) {
self.init(age: 0, name: name)
}
}
class Student : Person {
init(no: Int) {
super.init()
}
//重寫了父類的init(age: Int, name: String),有了指定初始化器的實現
override convenience init(age: Int, name: String) {
self.init(no: 0)
}
//重寫了父類的init(),有了指定初始化器的實現
override convenience init() {
self.init(no: 0)
}
}
//子類以便捷初始化器的形式重寫父類的指定初始化器,由此提供了父類所有指定初始化器的實現, 就會自動繼承所有的父類便捷初始化器
var stu1 = Student(age: 0, name: "Jack")
var stu2 = Student(age: 0)
var stu3 = Student(name: "Jack")
var stu4 = Student()
var stu5 = Student(no: 0)
required
- 用required修飾指定初始化器,表明其所有子類都必須實現該初始化器(通過繼承或者重寫實現)
- 如果子類重寫了required初始化器,也必須加上required,不用加override
屬性觀察器
- 父類的屬性在它自己的初始化器中賦值不會觸發屬性觀察器,但在子類的初始化器中賦值會觸發屬性觀察器
可以看出willSet的newValue爲1,所以屬性觀察器是由Student中的self.age = 1觸發的
可失敗初始化器
- 類、結構體、枚舉都可以使用init?定義可失敗初始化器
- 之前接觸過的可失敗初始化器
由圖可以看出,Int("123")方法的底層是 init?(_ description: String)
- 不允許同時定義參數標籤、參數個數、參數類型相同的可失敗初始化器和非可失敗初始化器
- 可以用init!定義隱式解包的可失敗初始化器
class Person {
var name: String
init!(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
}
var p1 = Person(name: "Jack")
//Optional(TestSwift.Person)
print(p1)
- 可失敗初始化器可以調用非可失敗初始化器,非可失敗初始化器調用可失敗初始化器需要進行解包
可失敗初始化器調用非可失敗初始化器:
class Person {
var name: String
//可失敗初始化器
convenience init?(name: String) {
self.init()
if name.isEmpty {
return nil
}
// self.name = name
}
//非可失敗初始化器
init() {
self.name = ""
}
}
非可失敗初始化器調用可失敗初始化器:
//第一種寫法
class Person {
var name: String
//可失敗初始化器
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
//非可失敗初始化器
convenience init() {
self.init(name: "")!
}
}
//第二種隱式解包寫法
class Person {
var name: String
//可失敗初始化器
init!(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
//非可失敗初始化器
convenience init() {
self.init(name: "")
}
}
- 如果初始化器調用一個可失敗初始化器導致初始化失敗,那麼整個初始化過程都失敗,並且之後的代碼都停止執行
- 可以用一個非可失敗初始化器重寫一個可失敗初始化器,但反過來是不行的
非可失敗重寫可失敗:
class Person {
var name: String
//可失敗初始化器
init?(name: String) {
if name.isEmpty {
return nil
}
self.name = name
}
//可失敗初始化器
convenience init?() {
self.init(name: "")
self.name = "Jack"
}
}
class Student: Person {
override init(name: String) {
super.init(name: name)!
}
}
可失敗重寫非可失敗會報錯:
反初始化器(deinit)
- deinit叫做反初始化器,類似於C++的析構函數、OC中的dealloc方法
- 當類的實例對象被釋放內存時,就會調用實例對象的deinit方法
- deinit不接受任何參數,不能寫小括號,不能自行調用
- 父類的deinit能被子類繼承
- 子類的deinit實現執行完畢後會調用父類的deinit
class Person {
deinit {
print("Person銷燬了")
}
}
class Student: Person {
deinit {
print("Student銷燬了")
}
}
func test() {
var stu = Student()
}
print("1")
test()
print("2")
//輸出結果爲:
1
Student銷燬了
Person銷燬了
2
Person和Person.self
這種情況下用法相同:
class Person {
static var age = 0
static func run() { }
}
Person.age = 20
Person.run()
var p0 = Person() //init()
var p1 = Person.self()
var p2 = Person.init()
var p3 = Person.self.init()
print(p0,p1,p2,p3)
這種情況下不同: