今天在參看Floral這個項目的時候,發現了UIButton的一種擴展方式。
i發現之前一直不怎么重視convenience這個關鍵字,于是參閱了一些資料進行了整理。
swfit有超級嚴格的初始化方法。一方面,Swift 強化了 designated 初始化方法的地位。Swift 中不加修飾的 init 方法都需要在方法中保證所有非 Optional 的實例變量被賦值初始化,而在子類中也強制 (顯式或者隱式地) 調用super版本的 designated 初始化,所以無論如何走何種路徑,被初始化的對象總是可以完成完整的初始化的。
在上面的示例代碼中,注意在init里我們可以對let的實例常量進行賦值,這是初始化方法的重要特點。在 Swift 中let聲明的值是常量,無法被寫入賦值,這對于構建線程安全的 API 十分有用。而因為 Swift 的init只可能被調用一次,因此在init中我們可以為常量進行賦值,而不會引起任何線程安全的問題。
與 designated 初始化方法對應的是在init前加上convenience關鍵字的初始化方法。這類方法是 Swift 初始化方法中的 “二等公民”,只作為補充和提供使用上的方便。所有的convenience初始化方法都必須調用同一個類中的 designated 初始化完成設置,另外convenience的初始化方法是不能被子類重寫或者是從子類中以super的方式被調用的。
只要在子類中實現重寫了父類convenience方法所需要的init方法的話,我們在子類中就也可以使用父類的convenience初始化方法了。比如在上面的代碼中,我們在ClassB里實現了init(num: Int)的重寫。這樣,即使在ClassB中沒有bigNum版本的convenience init(bigNum: Bool),我們仍然還是可以用這個方法來完成子類初始化:
因此進行一下總結,可以看到初始化方法永遠遵循以下兩個原則:
初始化路徑必須保證對象完全初始化,這可以通過調用本類型的 designated 初始化方法來得到保證;
子類的 designated 初始化方法必須調用父類的 designated 方法,以保證父類也完成初始化。
對于某些我們希望子類中一定實現的 designated 初始化方法,我們可以通過添加required關鍵字進行限制,強制子類對這個方法重寫實現。這樣做的最大的好處是可以保證依賴于某個 designated 初始化方法的convenience一直可以被使用。一個現成的例子就是上面的 init(bigNum: Bool):如果我們希望這個初始化方法對于子類一定可用,那么應當將init(num: Int)聲明為必須,這樣我們在子類中調用init(bigNum: Bool)時就始終能夠找到一條完全初始化的路徑了
另外需要說明的是,其實不僅僅是對 designated 初始化方法,對于 convenience 的初始化方法,我們也可以加上required以確保子類對其進行實現。這在要求子類不直接使用父類中的 convenience 初始化方法時會非常有幫助。
其實以上的知識點無非就是個便利制造器鏈的知識,我們可以做如下歸納:
init鏈:
為了簡化指定構造器和便利構造器之間的調用關系,Swift 采用以下三條規則來限制構造器之間的代理調用:
規則 1
designated必須調用其直接父類的的指定構造器。
規則 2
convenience必須調用同一類中定義的其它構造器。
規則 3
convenience必須最終以調用一個指定構造器結束。
一個更方便記憶的方法是:
designated必須總是向上代理
convenience必須總是橫向代理