防止絕技失傳的 required initializer

定義 Swift 類別時,子類別預設並不會繼承父類別的 initializer,除非滿足某些特殊的條件。

class Baby {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
}

class SuperBaby: Baby {
var magic: String
init(magic: String, name: String) {
self.magic = magic
super.init(age: 1, name: name)
}
}

比方以上例子,SuperBaby 繼承 Baby,但我們只能使用參數 magic & name 產生 SuperBaby 物件,不能使用參數 age & name,因為 SuperBaby 沒有繼承得到 Baby 的 init(age: Int, name: String)。

一定要有的 required initializer

但如果你真的想用盡洪荒之力,防止父類別的 initializer 失傳,想要繼續用 age & name 建立 SuperBaby,其實是做得到的。

只要在 initializer 前加上 required(required 是需要的意思),即可要求子類別一定得定義 initializer,防止initializer 失傳(若是子類別順利繼承 initializer,因為不會失傳,此時可不定義)。

因此,若我們不想讓 init(age: Int, name: String) 失傳,我們可以在它前面加上 required,如此當 SuperBaby 沒有定義它時將產生錯誤。

class Baby {
var age: Int
var name: String
required init(age: Int, name: String) {
self.age = age
self.name = name
}
}

class SuperBaby: Baby {
var magic: String
init(magic: String, name: String) {
self.magic = magic
super.init(age: 1, name: name)
}
}

錯誤訊息清楚地表明我們的子類別也要定義 init(age:name:)。

'required' initializer 'init(age:name:)' must be provided by subclass of 'Baby'

因此,我們只要乖乖聽話,定義 init(age:name:) 即可修正錯誤。

class SuperBaby: Baby {
var magic: String

init(magic: String, name: String) {
self.magic = magic
super.init(age: 1, name: name)
}

required init(age: Int, name: String) {
magic = "開發 Swift iOS App"
super.init(age: age, name: name)
}
}

有個小地方要特別注意,子類別定義 init(age:name:) 時還是要加上 required,否則將出現錯誤,因為加了 required 才能確保 Baby 的子子孫孫類別都有定義 init(age:name:),比方繼承 SuperBaby 的 SuperSuperBaby 也會定義 init(age:name:)。

init(age: Int, name: String) {
magic = "開發 Swift iOS App"
super.init(age: age, name: name)
}

忘了加上 required 的可怕紅色錯誤。

‘required’ modifier must be present on all overrides of a required initializer

iOS SDK 的 required initializer

開發 iOS App 時,有時我們會在繼承 iOS SDK 內建類別時遇到需要定義 required init 的情況,比方 UIViewController & UIView 的 init?(cooder:) 都是 required init,若我們在繼承 UIViewController & UIView 的類別自訂 init,必須再定義 init?(cooder:)。

以下我們實際看個 ColorView 的例子。我們在 ColorView 裡定義 init(color:frame:),想在生成元件時指定大小位置跟顏色。

class ColorView: UIView {
init(color: UIColor, frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = color
}
}

然而這時卻出現了紅色錯誤。

這是因為 UIView 的 init(coder:) 是 required,所以我們一定要定義。

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

不過如果 ColorView 裡沒有寫任何 init,反而不會有任何錯誤。這是因為當我們沒寫 init 時,ColorView 將繼承 UIView 的 init(coder:),因此我們不用再自己定義。而當我們在 ColorView 裡另外定義 init 時,它就不會再繼承 UIView 的 init(coder:),因此這時我們得自己定義 init(coder:)。

class ColorView: UIView {

}

關於 init 繼承的相關說明,有興趣的朋友可進一步參考以下連結裡 Automatic Initializer Inheritance 的段落。

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com