class を継承するときの initializer を説明します。
全3回で説明してます。その2回目です。
[Swift] 脱初心者 class initializer おさらい(1: designated initializer と convenience initializer)
[Swift] 脱初心者 class initializer おさらい(3: failable initializer)
Sponsor Link
クラスの継承
Initializer で初期化が必要な property を持つ親クラス、子クラスを以下のように定義しました。
import Foundation
class ParentClass {
var pProp:Int
init(_ value: Int) {
pProp = value
}
convenience init(value:Int, coeff: Int) {
self.init(value * coeff)
}
}
class ChildClass : ParentClass {
var cProp:Int
}
ParentClass には、convenience initializer も定義しています。
ChildClass には、initializer を定義していませんので、現時点ではコンパイルでエラーになります。
Class 'ChildClass' has no initializers
(意訳) ChildClass に initializer がありません。
子クラス側での考え方
initializer によって初期化が必要な property は、親クラスの持つ property と子クラスの持つ property です。
子クラス自身の持つ property の初期化
クラスの initizlier の考え方がそのまま適用されます。
つまり、初期化が必要な property を初期化するための initializer の定義が必要となります。
ChildClass で初期化が必要な property は、cProp ですので、以下のような initializer を作成し、designated initializer としました。
class ChildClass : ParentClass {
var cProp:Int
init(cValue: Int) {
cProp = cValue
}
}
現時点では、ChildClass の property だけしか初期化されていませんので、エラーとなります。
'super.init' isn't called on all paths before returning from initializer
(意訳) super.init が呼ばれていない
親クラス(ParentClass) の property も初期化が必要ですので、このエラーの理由は明らかです。
親クラスの持つ property の初期化
ChildClass の initializer から、親クラスの property を直接設定することはできません。子クラスの initializer から 親クラスの initializer を呼ぶことで、親クラスの property を初期化します。
子クラスの initializer では、親クラスの initializer を呼ぶことで、親クラスの property を初期化する
class ChildClass : ParentClass {
var cProp:Int
init(pValue:Int, cValue: Int) {
cProp = cValue
super.init(pValue)
}
}
ここでポイントが1つあります。
Super.init は、子クラスの property の初期化後に呼ぶ必要があります。
子クラスの initializer では、親クラスの initializer は、子クラス property の初期化後に呼ぶ
順序を逆にするとエラーとなります。
class ChildClass : ParentClass {
var cProp:Int
init(pValue:Int, cValue: Int) {
super.init(pValue) // error : Property 'self.cProp' not initialized at super.init call
cProp = cValue
}
}
designated initializer と convenience initializer
クラスの initializer では、convenience initializer を定義することができましたが、その中からは designated initializer をコールする必要がありました。
子クラスの designated initializer からは、親クラスの designated initializer を呼ぶ
実際に、子クラスから、親クラスの convenience initializer を呼ぶとエラーとなります。
class ChildClass : ParentClass {
var cProp:Int
init(pValue:Int, cValue: Int) {
cProp = cValue
super.init(value: 3, coeff: 4) // error : Must call a designated initializer of the superclass 'ParentClass'
}
}
親クラス側から指定できること
親クラスでの定義で、子クラス側に、initializer の override が必要であることを指定することができます。
これは、”required” というキーワードと共に initializer を定義することで指定します。
この “required” initializer は、designated initializer / convenience initializer のどちらにも指定することが可能です。
子クラス側で実装するときに、同じタイプ(designated initializer/ convenience initializer)であることは必要ありません。
Storyboard を使って UI を作成した場合には、作成した要素は、storyboard に保存され、実行時に読み込まれます。
保存する時や実行時に、encode/decode オブジェクトとのやり取りが必要になりますので、そのための initializer の定義が必須となります。
そのため、UIKit のクラスの多くが、以下のような定義を持っています。
required init?(coder aDecoder: NSCoder) {
// ....
}
こんな場合は?
親 class の property を、子 class の initializer で設定できる?
答え:できません。アクセスしようとすると、親クラスの property が、super.init 実行前に使用されたとエラーになります。
class ChildClass : ParentClass {
var cProp:Int
init(pValue:Int, cValue: Int) {
cProp = cValue
self.pProp = cValue // error: 'self' used in property access 'pProp' before 'super.init' call
super.init(3)
}
}
親 class の convenience initializer を、子 class の initializer から呼んでも良い?
答え:できません。designated initializer を呼んでくださいというエラーになります。
class ChildClass : ParentClass {
var cProp:Int
init(pValue:Int, cValue: Int) {
cProp = cValue
super.init(value: 3, coeff: 4) // error: Must call a designated initializer of the superclass 'ParentClass'
}
}
まとめ:クラス継承時の initializer
継承関係のクラスの initializer を考える上では以下の点がポイントとなります。
- 親クラス、子クラス それぞれの property はそれぞれが初期化する
- 子クラスの designated initializer から呼び出すことができるのは、親クラスの designated initializer だけ
- override されることが必須の initializer には、required キーワードを設定する
個別の項目をきちんと理解することも大切ですが、全体を俯瞰して理解することも大事です。俯瞰した理解には、以下のような書籍を使うのが近道です。
Swift 学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
現時点では、上記の Swift 5 に対応した第5版が最新版です。
Swift ポケットリファレンス
Swift を学んでも、プログラミング言語の文法を全て記憶しておくことは無理なので、ちょっとした文法の確認をするために、リファレンス本を手元に置いておくと便利です。
Swift4 までしか対応していないので、理解して参照する必要があります。Swift 5 に対応したリファレンス本がでるまでは、一番有用です。
説明は以上です。
不明な点、おかしな点は、お気軽にご連絡ください。
Sponsor Link