歡迎加入QQ討論群258996829
來自星星的你 頭像
蘋果3袋
3
來自星星的你

Swift的初始化方法

發(fā)布時(shí)間:2015-01-08 20:54  回復(fù):1  查看:3803   最后回復(fù):2015-01-08 20:57  

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

其實(shí)就是安全。在Objective-C中,init方法是非常不安全的:沒有人能保證init只被調(diào)用一次,也沒有人保證在初始化方法調(diào)用以后,實(shí)例的各個(gè)變量都完成初始化,甚至如果在初始化里使用屬性進(jìn)行設(shè)置的話,還可能會造成各種問題。雖然Apple也明確說明了不應(yīng)該在init中使用屬性來訪問,但這并不是編譯器強(qiáng)制的,因此還是會有很多開發(fā)者犯這樣的錯(cuò)誤。

所以Swift有了超級嚴(yán)格的初始化方法。一方面,Swift強(qiáng)化了designated初始化方法的地位。Swift中不加修飾的init方法都需要在方法中保證所有非Optional的實(shí)例變量被賦值初始化,而在子類中也強(qiáng)制 (顯式或隱式地)調(diào)用super版本的designated初始化,所以無論如何走何種路徑,被初始化的對象總是可以完成完整的初始化的。


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的實(shí)例常量進(jìn)行賦值,這是初始化方法的重要特點(diǎn)。在Swift中l(wèi)et聲明的值是不變量,無法被寫入賦值,這對于構(gòu)建線程安全的API十分有用。而因?yàn)镾wift的init只可能被調(diào)用一次,因此在init中我們可以為不變量進(jìn)行賦值,而不會引起任何線程安全的問題。

與designated初始化方法對應(yīng)的是在init前加上convenience關(guān)鍵字的初始化方法。這類方法是Swift初始化方法中的“二等公民”,只作為補(bǔ)充和提供使用上的方便。所有的convenience初始化方法都必須調(diào)用同一個(gè)類中的designated初始化完成設(shè)置,另外convenience的初始化方法是不能被子類重寫或從子類中以super的方式被調(diào)用的。

class ClassA {  
    let numA: Int  
    init(num: Int) {  
        numA = num  
    }  
    convenience init(bigNum: Bool) {  
        self.init(num: bigNum ? 10000 : 1)  
    }  
}  
class ClassB: ClassA {  
    let numB: Int  
    override init(num: Int) {  
        numB = num + 1  
        super.init(num: num)  
    }  
}  
只要在子類中實(shí)現(xiàn)重寫了父類convenience方法所需要的init方法的話,我們在子類中就也可以使用父類的convenience初始化方法了。比如在上面的代碼中,我們在ClassB里實(shí)現(xiàn)了init(num: Int)的重寫。這樣,即使在ClassB中沒有bigNum版本的convenience init(bigNum: Bool),我們?nèi)匀贿€是可以用這個(gè)方法來完成子類初始化:

let anObj = ClassB(bigNum: true)  
// anObj.numA = 10000, anObj.numB = 10001  

因此進(jìn)行一下總結(jié),可以看到初始化方法永遠(yuǎn)遵循以下兩個(gè)原則:

  1. 初始化路徑必須保證對象完全初始化,這可以通過調(diào)用本類型的designated初始化方法來得到保證;
  2. 子類的designated初始化方法必須調(diào)用父類的designated方法,以保證父類也完成初始化。

對于某些我們希望子類中一定實(shí)現(xiàn)的designated初始化方法,我們可以通過添加required關(guān)鍵字進(jìn)行限制,強(qiáng)制子類對這個(gè)方法重寫實(shí)現(xiàn)。這樣的一個(gè)最大的好處是可以保證依賴于某個(gè)designated初始化方法的convenience一直可以被使用。一個(gè)現(xiàn)成的例子就是上面的init(bigNum: Bool):如果我們希望這個(gè)初始化方法對于子類一定可用,那么應(yīng)當(dāng)將init(num: Int)聲明為必須,這樣我們在子類中調(diào)用init(bigNum: Bool)時(shí)就始終能夠找到一條完全初始化的路徑了:

class ClassA {  
    let numA: Int  
    required init(num: Int) {  
        numA = num  
    }  
    convenience init(bigNum: Bool) {  
        self.init(num: bigNum ? 10000 : 1)  
    }  
}  
class ClassB: ClassA {  
    let numB: Int  
    required init(num: Int) {  
        numB = num + 1  
        super.init(num: num)  
    }  
}  
另外需要說明的是,其實(shí)不僅僅是對designated初始化方法,對于convenience的初始化方法,我們也可以加上required以確保子類對其進(jìn)行實(shí)現(xiàn)。這在要求子類不直接使用父類中的convenience初始化方法時(shí)會非常有幫助。

來自星星的你 頭像
蘋果3袋
3
來自星星的你   2015-01-08 20:57
 本文作者:王?。ˊonevcat),iOS和Unity3D開發(fā)者。
您還未登錄,請先登錄

熱門帖子

最新帖子

?