イニシャライザから呼び出せる機能まとめ

Swift プログラミング

Swift のイニシャライザでは初期化前の値が使われないように、使える機能が制限されています。

幾らかの段階を経てイニシャライザ内でも全ての機能が使えるようになるので、どの段階でどの機能が利用できるのか整理してみました。


Swift言語 の構造体 やクラス では、イニシャライザを使ってオブジェクトを生成します。

イニシャライザでは構造体やクラスが持つストアドプロパティ の値を初期化して利用準備を整えますが、すべてのストアドプロパティが初期化されるまでは、自分自身に実装されている機能の一部を利用することができません。

幾つかの段階を経て、イニシャライザの中でも自身の全ての機能を利用できるようになるので、その辺りを整理しておくことにしました。

特筆する場面を除いて、構造体でもクラスでも同じです。

ストアドプロパティへの書き込みはいつでも行える

値を保持できるストアドプロパティへの書き込みは、イニシャライザの中でいつでも行えます。

struct MyStruct {
	
	var stored:Int
	
	init() {
	
		// ストアドプロパティへの書き込みは常に行えます。
		self.stored = 10
	}
}

ストアドプロパティの参照はそれぞれの初期化後から利用できる

イニシャライザ内では、既に値が書き込まれたストアドプロパティだけを参照できます。

書き込み終わったストアドプロパティであっても、それにコンピューティッドプロパティを介してアクセスすることはできません。

struct MyStruct {
	
	var stored1:Int
	var stored2:Int
	
	var computed1 {
		
		return self.stored1
	}
	
	init() {
	
		// 未初期化のストアドプロパティは参照できません。
		// self.stored1 = self.stored2
		
		// 初期化済みでもコンピューティッドプロパティ経由では参照できません。
		// self.stored1 = 10
		// self.stored2 = self.computed1
	}
}

上記のどちらを実行しようとしたときも、次のようなエラーが発生します。

Variable 'self.stored2' used before being initialized

エラーメッセージで挙げられる可変値名は、初期化前のストアドプロパティを右辺で使った場合はそのプロパティ名に、コンピューティッドプロパティを右辺で使った場合は未初期化のストアドプロパティ名のどれかになります。

コンピューティッドプロパティとメソッドは全てを初期化した後で呼び出す

コンピューティッドプロパティやメソッドは、ストアドプロパティを使って何か処理を行うことになりますが、これらについては全てのストアドプロパティが初期化された以降から利用できるようになります。

struct MyStruct {
	
	var stored1:Int
	var stored2:Int
	
	var computed1 {
		
		get {
			
			return self.stored1
		}
		
		set {
			
			self.stored1 = newValue
		}
	}
	
	func method() {
		
	}
	
	init() {
	
		self.stored1 = 10
		self.stored2 = 20
		
		// 全てのストアドプロパティが初期化された後であれば
		// メソッドやコンピューティッドを利用できます。
		
		self.method()
		self.computed1 = 30
	}
}

全てのストアドプロパティが初期化される前は、たとえストアドプロパティを設定するメソッドやコンピューティッドプロパティであっても呼び出せません。

初期化で使いたい機能は静的メソッドで実装する

初期化処理をメソッドに実装して複数のイニシャライザで使いたいようなときは、通常のメソッドは呼び出せないので、静的メソッドか大域関数で実装する必要があります。

struct MyStruct {
	
	var stored:Int
	
	private static func initializeMethod() -> Int {
		
		return 10
	}
	
	init() {

		// 静的メソッドや大域関数であれば初期化前から呼び出せます。	
		self.stored = MyStruct.initializeMethod()
	}
}

クラスの場合はstatic func のところがclass func になります。

自身の別のイニシャライザは初期化前に呼び出す

自身の別のイニシャライザを呼び出すときは、自身のストアドプロパティをひとつも初期化しないうちに呼び出す必要があります。

struct MyStruct {
	
	var stored:Int
	
	init(value:Int) {
		
		self.stored = value
	}
	
	init() {

		// イニシャライザを呼び出す前にストアドプロパティの初期化はできません。
		// self.stored = 10

		// ここで自身の別のイニシャライザを呼び出したとします。
		self.init(value:20)
		
		// これ以降なら、必要であれば、ストアドプロパティの再初期化も行えます。
		self.stored = 30
	}
}

イニシャライザを呼び出した後は全てのストアドプロパティが初期化されているので、メソッドやコンピューティッドプロパティを呼び出せます。ストアドプロパティの再初期化も可能です。


なお、イニシャライザを呼び出す前にストアドプロパティを初期化しようとすると、次のようなエラーになります。

Use of 'self' in delegating initializer before self.init is called

クラスの場合でイニシャライザ内で自分自身の別のイニシャライザを呼び出すときには、呼び出し元のイニシャライザをconvenience init で定義します。構造体の場合はconvenience は不要です。

基底クラスのイニシャライザは初期化後に呼び出す

クラスの場合、他のクラスを継承している場合があります。

この場合は親クラスのイニシャライザを呼び出す前に、自身に定義されたストアドプロパティを全て初期化しておく必要があります。

class SubClass : BaseClass {
	
	var stored1:Int
	var stored2:Int
	
	override init() {
		
		self.stored1 = 10
		self.stored2 = 20
		
		// 親クラスのイニシャライザを呼び出す前に
		// 全てのストアドプロパティを初期化しておかないとエラーになります。
		
		super.init()
	}
}

初期化をせずに親クラスのイニシャライザを呼び出そうとすると次のようなエラーになります。

Property 'self.stored' not initialized at super.init call

オプショナル型は暗黙的に初期化される

原則としてストアドプロパティは全て初期化する必要がありますが、オプショナル型に限っては、何もしなくてもnil で初期化されます。

そのため、オプショナル型のストアドプロパティへの代入文を何も書かなくても、初期化が終わったものとしてコードを書き進められます。

struct MyStruct {
	
	var stored:Int?
	
	func method() {
		
	}
	
	init() {
	
		// オプショナル型のストアドプロパティを明示的に初期化していなくても、
		// 初期化が完了しているものとしてメソッド呼び出しなどを利用できます。
		
		self.method()
	}
}

オプショナル型はOptional<T> でもImplicitlyUnwrappedOptional<T> でも上記のとおりに初期化されます。

初期値でストアドプロパティを初期化することも可能

ストアドプロパティは、その宣言時に初期値を指定できるようになっています。

初期値を与えればイニシャライザ内のコードを実行する直前に自動で初期化が行われるため、初期化が終わった後のコードから書き進められます。

struct MyStruct {
	
	var stored:Int = 10
	
	func method() {

	}
	
	init() {
	
		// 初期値を指定したストアドプロパティは
		// イニシャライザが呼ばれたときには初期化が完了しているため、
		// 初期化後から利用できるようになるメソッド呼び出しなどを利用できます。
		
		self.method()
	}
}

ストアドプロパティに初期値を設定した状態でイニシャライザでそのプロパティに値を設定すると、初期化が終わった後に再び値が設定されることになります。