初始化方法順序
與 Objective-C 不同,Swift 的初始化方法需要保證類型的所有屬性都被初始化。所以初始化方法的調(diào)用順序就很有講究。在某個類的子類中,初始化方法里語句的順序并不是隨意的,我們需要保證在當前子類實例的成員初始化完成后才能調(diào)用父類的初始化方法。
class Cat {
var name: String
init() {
name = "cat"
}
}
class Tiger: Cat {
let power: Int
override init() {
power = 10
super.init()
name = "tiger"
}
}
一般來說,子類的初始化順序是:
- 設置子類自己需要初始化的參數(shù),
power = 10
- 調(diào)用父類的相應的初始化方法,
super.init()
- 對父類中的需要改變的成員進行設定,
name = "tiger"
如果在子類中不需要對父類的成員做出改變的話就不存在第 3 步。這種情況下,第2步的super.init()
也不需要手動調(diào)用了,Swift可以自動的幫我們完成。于是上面的代碼可以改成如下的形式:
class Cat {
var name: String
init() {
name = "cat"
}
}
class Tiger: Cat {
let power: Int
override init() {
power = 10
// 如果我們不需要改變 name 的話,
// 雖然我們沒有顯式地對 super.init() 進行調(diào)用
// 不過由于這是初始化的最后了,Swift 替我們自動完成了
}
}
DESIGNATED,CONVENIENCE 和 REQUIRED
在Objective-C中,init方法是非常不安全的,因為沒有人能保證init方法只被調(diào)用一次,也沒有人保證在初始化方法調(diào)用以后實例的各個變量都完成初始化,甚至如果在初始化里使用屬性進行設置的話,還可能會造成各種問題
所以 Swift 有了超級嚴格的初始化方法。一方面,Swift 強化了 designated 初始化方法的地位。Swift 中不加修飾的 init 方法都需要在方法中保證所有非 Optional 的實例變量被賦值初始化,而在子類中也強制 (顯式或者隱式地) 調(diào)用 super 版本的 designated 初始化,所以無論如何走何種路徑,被初始化的對象總是可以完成完整的初始化的。
class ClassA {
let numA: Int
init(num: Int) {
numA = num
}
convenience init(bigNum: Bool) {
self.init(num: bigNum ? 1000 : 1)
}
}
class ClassB: ClassA {
let numB: Int
/*
只要在子類中實現(xiàn)重寫了父類convenience方法所需要的init方法的話,
我們在子類中就也可以使用父類的convenience初始化方法了。
*/
override init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
let bObj = ClassB(bigNum: true)
bObj.numA // 1000
bObj.numB // 1001
從上面的代碼可以看出,init方法可以對let變量進行賦值操作,這是初始化方法最重要的特點。因為 Swift 的 init 只可能被調(diào)用一次,因此在 init 中我們可以為常量進行賦值,而不會引起任何線程安全的問題。
convenience
關鍵字修飾的init方法是用于補充和提供使用上的方便。所有的 convenience 初始化方法都必須調(diào)用同一個類中的 designated 初始化完成設置,另外 convenience 的初始化方法是不能被子類重寫或者是從子類中以 super 的方式被調(diào)用的。
對于某些我們希望子類中一定實現(xiàn)的 designated 初始化方法,我們可以通過添加 required
關鍵字進行限制,強制子類對這個方法重寫實現(xiàn)。
這樣做的最大的好處是可以保證依賴于某個 designated 初始化方法的 convenience 一直可以被使用。
如果我們不使用required
關鍵字來限制的話,那么下面的ClassC這個類就無法調(diào)用父類中的init(bigNum: Bool)
方法
class ClassC: ClassB {
let numC: Int
init(cNum value: Int) {
numC = value
super.init(num: 100)
}
}
// 這里已經(jīng)不能調(diào)用ClassA中的convenience初始化方法
let cObj = ClassC(cNum: 10)
cObj.numA // 100
cObj.numB // 101
cObj.numC // 10
另外需要說明的是,其實不僅僅是對 designated 初始化方法,對于 convenience 的初始化方法,我們也可以加上 required 以確保子類對其進行實現(xiàn)。這在要求子類不直接使用父類中的 convenience 初始化方法時會非常有幫助。