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

class 等を定義していると、なんとなくclass の initializer も作れて使えてしまいますが、コンパイラからのワーニングやエラーに従って修正することでなんとなく使っている人も多いと思います。
改めて、initializer を説明します。

initializer とは

Initializer は、class や struct を初期化するために使用されます。

init という名前で定義される関数で、class 等のもつ property を初期設定するために使用されます。

property の設定が途中段階で使用されてしまうとバグの温床になってしまうため、initializer の処理が終わったときには、すべての property に対して、設定が終了していることが必要です。

以下の説明では、class の initializer に焦点をあてて、説明していきます。

property 概要

property
property とは、class 等の保持する情報を表す変数・定数等のことです。

property 定義例
上記は、”SampleClass” というクラスが、”information” という Int 型のプロパティを持つことを定義しています。
このまま使ってしまうと、information というプロパティは、不定の値を持つことになってしまうため、どこかで初期化することが必要となります。

最初に理解すべき3つの initializer

initializer には様々な種類がありますが、まずは以下の3つの initializer を理解することが大事です。

  • default initializer
  • designated initializer
  • convenience initializer

1つ1つ説明していきます。

default initializer

class 定義時に property に対して default 値を指定することができます。

default 値を持つ property を持つ class 定義

class の持つすべての property に対して、default 値が設定されているときには、
default initializer というものが自動で用意されます。

自動で用意されるために、init() …. というようなコードはなくとも、下記のように init() として呼び出すことができます。

default initializer 使用例

このときには、property には、default 値がセットされます。

designated initializer

class が 初期設定を必要とする property を持つ場合は、その property を初期化する initializer を用意する必要があります。

初期設定を必要とする property とは?

その property が初期設定を必要とするかどうかは、property が、どのように定義されているかで変わります。

  • stored property / computed property のどちらで定義されているか
  • let / var のどちらで定義されているか
  • optional / non-optional のどちらで定義されているか

その property の定義により、initializer で設定が必要になるものならないものがあります。

property default値 設定必要? 設定値
stored property let non-optional default値あり 不要 default 値
stored property let non-optional default値なし 必要 指定値
stored property let optional default値あり 不要 default 値
stored property let optional default値なし 必要 指定値
stored property var optional default値あり 不要 default 値
stored property var optional default値なし 必要 指定値
stored property var non-optional default値あり 不要 default 値
stored property var non-optional default値なし 不要 nil
computed property 不要 設定不可
example code

上記の designated initializer (init) 内では、設定が必要となるプロパティ (1), (2), (3) のみを設定しています。

設定しないと不定値になってしまうため、コンパイラエラーとなります。なお、var h:Int については、default 値が設定されていませんが、optional Int であるため、nil が設定されます。

convenience initializer

designated initializer の他に、convenience initializer というものを定義することができます。

この convenience initializer は、以下の特徴を持ちます。

  • convenience initializer を用意するかどうかは自由
  • init 処理の一部を delegate できる

名前の通り、convenience (便利)のための initializer です。

initializer 内部では、その class/struct の設定が終了していないため、method 呼び出しや property の参照が制限されますが、
convenience initializer からは、他の initializer (designated initializer, convenience initializer いずれも)呼び出すことができます。
(このことを delegate と呼びます)

このことを使って、例えば初期化処理についても共通処理を導入することができます。

疑問点:いろいろと確認してみました

複数の designated initializer を作っても良い?

答え:作れます (引数が異なる init にする必要があります。)

example code
上記では、init(), init(b:), init(d:) が designated initializer となります。

designated initializer を複数 init に分けて作ってもよい?

答え:作れない (convenience initializer のみ、自 class 内の他の init を呼ぶことができます)

2つの designated initializer を作って、1つから他方を呼ぶようにしてみました。

example code

コンパイル時に以下のエラーが発生します。

error

(意訳)SampleClass の designated initializer は、delegate できません。convenience initializer を作りたかった?

(訳注)init 内から別の init を呼ぶことを delegate と呼びます。

default 値が設定されているときは、convenience init は、 designated initializer を呼ばなくて良い?

答え:呼ばないといけません。

example code

以下のようなエラーがコンパイル時に発生します。

error

(意訳)self.init を self を使う前に呼ばないといけないけど、self.init が initializer が終了するまでに呼ばれていない

convenience initializer からの designated initializer 呼び出しのタイミングは、自由?

答え:一番最初に、designated initializer を呼ばないといけません。

example code

以下のようなエラーがコンパイル時に発生します。

error

(意訳)self が self.init 呼び出しの前に使われました

次の段階で知りたくなる3つの initializer

ここまでの理解で、独自クラスを作って、その initializer を定義することができるようになります。

クラス定義を行って、開発を進めていくと、もう少し凝ったことをやりたくなり、以下のことが必要になることもあります。

  • super.init()
  • failable initializer
  • required initializer

厳密には、super.init は、initializer の種類ではありませんが、親クラスがあるときに、その initializer をどう扱うかということです。

failable initailzier は、失敗することがありえる initializer です。required initializer は、子クラスにたいしての特定の initializer の実装を要求するものです。

詳細は、別記事にする予定です。

まとめ: initializer

オブジェクトや構造体を正しく使用するためには、きちんと初期化されていることが重要です。

そのためには、initializer についてきちんと理解することが必要となります。

initializer 理解のポイント
  • default initializer, designated initializer, convenience initializer の使い分け
  • property 定義と初期化要否の関係

Xcode では、コンパイラが多くをチェックしてくれて、ワーニングやエラーで教えてくれますので、それにしたがって、修正して使うことも可能ですが、上記を理解すると、コンパイラ頼りのコーディングから脱することができます。

おかしな点、不明点ありましたら、お気軽に。

説明は以上です。

コメントを残す

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