[Swift] 脱初心者 class initializer おさらい(2: クラス継承と required initializer)

class の動作を理解するためには、initializer を理解することがポイントの1つです。

class を継承するときの initializer を説明します。

[Swift] 脱初心者 class initializer おさらい(1: designated initializer と convenience initializer)

クラスの継承

Initializer で初期化が必要な property を持つ親クラス、子クラスを以下のように定義しました。

ParentClass と ChildClass

ParentClass には、convenience initializer も定義しています。

ChildClass には、initializer を定義していませんので、現時点ではコンパイルでエラーになります。

コード

(意訳) ChildClass に initializer がありません。

子クラス側での考え方

initializer によって初期化が必要な property は、親クラスの持つ property と子クラスの持つ property です。

子クラス自身の持つ property の初期化

クラスの initizlier の考え方がそのまま適用されます。

つまり、初期化が必要な property を初期化するための initializer の定義が必要となります。

ChildClass で初期化が必要な property は、cProp ですので、以下のような initializer を作成し、designated initializer としました。

ChildClass に designated initializer を定義

現時点では、ChildClass の property だけしか初期化されていませんので、エラーとなります。

コード

(意訳) super.init が呼ばれていない

親クラス(ParentClass) の property も初期化が必要ですので、このエラーの理由は明らかです。

親クラスの持つ property の初期化

ChildClass の initializer から、親クラスの property を直接設定することはできません。子クラスの initializer から 親クラスの initializer を呼ぶことで、親クラスの property を初期化します。

親子クラスでの initializer のポイント
子クラスの initializer では、親クラスの initializer を呼ぶことで、親クラスの property を初期化する
example code

ここでポイントが1つあります。

Super.init は、子クラスの property の初期化に呼ぶ必要があります。

親子クラスでの initializer のポイント
子クラスの initializer では、親クラスの initializer は、子クラス property の初期化後に呼ぶ

順序を逆にするとエラーとなります。

エラーの発生するコード

designated initializer と convenience initializer

クラスの initializer では、convenience initializer を定義することができましたが、その中からは designated initializer をコールする必要がありました。

親子クラスでの initializer のポイント
子クラスの designated initializer からは、親クラスの designated initializer を呼ぶ

実際に、子クラスから、親クラスの convenience initializer を呼ぶとエラーとなります。

example code

親クラス側から指定できること

親クラスでの定義で、子クラス側に、initializer の override が必要であることを指定することができます。

これは、”required” というキーワードと共に initializer を定義することで指定します。

この “required” initializer は、designated initializer / convenience initializer のどちらにも指定することが可能です。

子クラス側で実装するときに、同じタイプ(designated initializer/ convenience initializer)であることは必要ありません

どんなときに使われる?
Storyboard を使って UI を作成した場合には、作成した要素は、storyboard に保存され、実行時に読み込まれます。

保存する時や実行時に、encode/decode オブジェクトとのやり取りが必要になりますので、そのための initializer の定義が必須となります。
そのため、UIKit のクラスの多くが、以下のような定義を持っています。

UIKit でよくみる initializer

こんな場合は?

親 class の property を、子 class の initializer で設定できる?

答え:できません。アクセスしようとすると、親クラスの property が、super.init 実行前に使用されたとエラーになります。

example code

親 class の convenience initializer を、子 class の initializer から呼んでも良い?

答え:できません。designated initializer を呼んでくださいというエラーになります。

example code

まとめ:クラス継承時の initializer

継承関係のクラスの initializer を考える上では以下の点がポイントとなります。

initializer のポイント
  • 親クラス、子クラス それぞれの property はそれぞれが初期化する
  • 子クラスの designated initializer から呼び出すことができるのは、親クラスの designated initializer だけ
  • override されることが必須の initializer には、required キーワードを設定する

説明は以上です。
不明な点、おかしな点は、お気軽にご連絡ください。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です