天天看點

Swift的初始化方法

我們在深入初始化方法之前,不妨先再想想swift中的初始化想要達到一種怎樣的目的。

其實就是安全。在objective-c中,init方法是非常不安全的:沒有人能保證init隻被調用一次,也沒有人保證在初始化方法調用以後,執行個體的各個變量都完成初始化,甚至如果在初始化裡使用屬性進行設定的話,還可能會造成各種問題。雖然apple也明确說明了不應該在init中使用屬性來通路,但這并不是編譯器強制的,是以還是會有很多開發者犯這樣的錯誤。

所 以swift有了超級嚴格的初始化方法。一方面,swift強化了designated初始化方法的地位。swift中不加修飾的init方法都需要在方 法中保證所有非optional的執行個體變量被指派初始化,而在子類中也強制 (顯式或隐式地)調用super版本的designated初始化,是以無論如何走何種路徑,被初始化的對象總是可以完成完整的初始化的。

[cpp] view plaincopy

Swift的初始化方法
Swift的初始化方法

class classa {  

    let numa: int  

    init(num: int) {  

        numa = num  

    }  

}  

class classb: classa {  

    let numb: int  

    override init(num: int) {  

        numb = num + 1  

        super.init(num: num)  

在 上面的示例代碼中,注意在init裡我們可以對let的執行個體常量進行指派,這是初始化方法的重要特點。在swift中let聲明的值是不變量,無法被寫入 指派,這對于建構線程安全的api十分有用。而因為swift的init隻可能被調用一次,是以在init中我們可以為不變量進行指派,而不會引起任何線 程安全的問題。

與designated初始化方法對應的是在init前加上convenience關鍵字的初始化方法。這類方法是 swift初始化方法中的“二等公民”,隻作為補充和提供使用上的友善。所有的convenience初始化方法都必須調用同一個類中的 designated初始化完成設定,另外convenience的初始化方法是不能被子類重寫或從子類中以super的方式被調用的。

Swift的初始化方法
Swift的初始化方法

    convenience init(bignum: bool) {  

        self.init(num: bignum ? 10000 : 1)  

隻 要在子類中實作重寫了父類convenience方法所需要的init方法的話,我們在子類中就也可以使用父類的convenience初始化方法了。比 如在上面的代碼中,我們在classb裡實作了init(num: int)的重寫。這樣,即使在classb中沒有bignum版本的convenience init(bignum: bool),我們仍然還是可以用這個方法來完成子類初始化:

Swift的初始化方法
Swift的初始化方法

let anobj = classb(bignum: true)  

// anobj.numa = 10000, anobj.numb = 10001  

是以進行一下總結,可以看到初始化方法永遠遵循以下兩個原則:

初始化路徑必須保證對象完全初始化,這可以通過調用本類型的designated初始化方法來得到保證;

子類的designated初始化方法必須調用父類的designated方法,以保證父類也完成初始化。

對 于某些我們希望子類中一定實作的designated初始化方法,我們可以通過添加required關鍵字進行限制,強制子類對這個方法重寫實作。這樣的 一個最大的好處是可以保證依賴于某個designated初始化方法的convenience一直可以被使用。一個現成的例子就是上面的 init(bignum: bool):如果我們希望這個初始化方法對于子類一定可用,那麼應當将init(num: int)聲明為必須,這樣我們在子類中調用init(bignum: bool)時就始終能夠找到一條完全初始化的路徑了:

Swift的初始化方法
Swift的初始化方法

    required init(num: int) {  

另外需要說明的是,其實不僅僅是對designated初始化方法,對于convenience的初始化方法,我們也可以加上required以確定子類對其進行實作。這在要求子類不直接使用父類中的convenience初始化方法時會非常有幫助。