改めて、initializer を説明します。
全3回で説明してます。その1回目です。
[Swift] 脱初心者 class initializer おさらい(2: クラス継承と required initializer)
[Swift] 脱初心者 class initializer おさらい(3: failable initializer)
Sponsor Link
initializer とは
Initializer は、class や struct を初期化するために使用されます。
init という名前で定義される関数で、class 等のもつ property を初期設定するために使用されます。
property の設定が終わる前に、使用が開始されてしまうとバグの温床になってしまうため、initializer の処理が終わったときには、すべての property に対して、設定が終了していることが必要です。
以下の説明では、class の initializer に焦点をあてて、説明していきます。
property 概要
property とは、class 等の保持する情報を表す変数・定数等のことです。
class SampleClass {
var informaiton:Int
}
上記は、”SampleClass” というクラスが、”information” という Int 型のプロパティを持つことを定義しています。
このまま使ってしまうと、information というプロパティは、不定の値を持つことになってしまうため、どこかで初期化することが必要となります。
最初に理解すべき3つの initializer
initializer には様々な種類がありますが、まずは以下の3つの initializer を理解することが大事です。
- default initializer
- designated initializer
- convenience initializer
1つ1つ説明していきます。
default initializer
class 定義時に property に対して default 値を指定することができます。
class SampleClass {
var information: Int = 25
}
class の持つすべての property に対して、default 値が設定されているときには、
default initializer というものが自動で用意されます。
自動で用意されるために、init() …. というようなコードはなくとも、下記のように init() として呼び出すことができます。
Let sampleClassInstance = SampleClass.init()
このときには、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 | 不要 | 設定不可 |
class SampleClass {
let a:Int = 3
let b:Int
let c:Int? = 3
let d:Int?
var e:Int = 3
var f:Int
var g:Int? = 3
var h:Int?
var i:Int {
return 3
}
init() { // designated initializer
self.b = 3 // (1)
self.d = 3 // (2)
self.f = 4 // (3)
}
}
上記の 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 にする必要があります。)
class SampleClass {
let a:Int = 3
let b:Int
let c:Int? = 3
let d:Int?
var e:Int = 3
var f:Int
var g:Int? = 3
var h:Int?
var i:Int {
return 3
}
init() {
self.b = 3
self.d = 3
self.f = 4
}
init(b:Int ) {
self.b = b
self.d = 3
self.f = 4
}
init(d:Int) {
self.b = 3
self.d = d
self.f = 4
}
}
上記では、init(), init(b:), init(d:) が designated initializer となります。
designated initializer を複数 init に分けて作ってもよい?
答え:作れない (convenience initializer のみ、自 class 内の他の init を呼ぶことができます)
2つの designated initializer を作って、1つから他方を呼ぶようにしてみました。
class SampleClass {
var a:Int
var b:Int
init(a: Int) {
self.a = a
self.b = 5
}
init(b: Int) {
self.init(a:4) // エラー designated init から 別の init を呼び出し
self.b = b
}
}
コンパイル時に以下のエラーが発生します。
Designated initializer for 'SampleClass' cannot delegate (with 'self.init'); did you mean this to be a convenience initializer?
(意訳)SampleClass の designated initializer は、delegate できません。convenience initializer を作りたかった?
(訳注)init 内から別の init を呼ぶことを delegate と呼びます。
default 値が設定されているときは、convenience init は、 designated initializer を呼ばなくて良い?
答え:呼ばないといけません。
class SampleClass {
var a:Int = 4
var b:Int = 5
convenience init(a: Int) {
self.a = a // error : designated initializer を呼び出していない
}
}
以下のようなエラーがコンパイル時に発生します。
self' used before 'self.init' call or assignment to 'self' 'self.init' isn't called on all paths before returning from initializer
(意訳)self.init を self を使う前に呼ばないといけないけど、self.init が initializer が終了するまでに呼ばれていない
convenience initializer からの designated initializer 呼び出しのタイミングは、自由?
答え:一番最初に、designated initializer を呼ばないといけません。
class SampleClass {
var a:Int = 3
var b:Int
init() {
self.b = 3
}
convenience init(a: Int) {
self.a = 11 // error : designated initializer 呼び出し前に、self にアクセス
self.init()
}
}
以下のようなエラーがコンパイル時に発生します。
'self' used before 'self.init' call or assignment to 'self'
(意訳)self が self.init 呼び出しの前に使われました
次の段階で知りたくなる3つの initializer
ここまでの理解で、独自クラスを作って、その initializer を定義することができるようになります。
クラス定義を行って、開発を進めていくと、もう少し凝ったことをやりたくなり、以下のことが必要になることもあります。
- super.init()
- failable initializer
- required initializer
厳密には、super.init は、initializer の種類ではありませんが、親クラスがあるときに、その initializer をどう扱うかということです。
failable initailzier は、失敗することがありえる initializer です。required initializer は、子クラスにたいしての特定の initializer の実装を要求するものです。
詳細は、別記事にする予定です。
まとめ: initializer
オブジェクトや構造体を正しく使用するためには、きちんと初期化されていることが重要です。
そのためには、initializer についてきちんと理解することが必要となります。
- default initializer, designated initializer, convenience initializer の使い分け
- property 定義と初期化要否の関係
Xcode では、コンパイラが多くをチェックしてくれて、ワーニングやエラーで教えてくれますので、それにしたがって、修正して使うことも可能ですが、上記を理解すると、コンパイラ頼りのコーディングから脱することができます。
個別の項目をきちんと理解することも大切ですが、全体を俯瞰して理解することも大事です。俯瞰した理解には、以下のような書籍を使うのが近道です。
Swift 学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
現時点では、上記の Swift 5 に対応した第5版が最新版です。
Swift ポケットリファレンス
Swift を学んでも、プログラミング言語の文法を全て記憶しておくことは無理なので、ちょっとした文法の確認をするために、リファレンス本を手元に置いておくと便利です。
Swift4 までしか対応していないので、理解して参照する必要があります。
おかしな点、不明点ありましたら、お気軽に。
説明は以上です。
Sponsor Link