Swiftのイニシャライザについて


# はじめに
SwiftにはClass, Structure, Enumerationと様々なものがありますが、その全てにイニシャライザというものがあります。
本記事ではこのイニシャライザに関する事柄について説明します。

# イニシャライザの種類

* Failable Initializer
* Default Initializer

Classに限り以下のイニシャライザがあります。

* Designated Initializer
* Convenience Initializer
* Required Initializer

また、Structureに限り以下のイニシャライザがあります。

* Memberwise Initializer

### Failable Initializer

初期化に失敗することができるイニシャライザで以下のように書きます

```swift

class Sample {

	// Optional<Sample>を返す
	init?() {
		// ...
	}

	// ImplicitlyUnwrappedOptional<Sample>を返す
	init!() {
		// ...
	}

```

Swiftのイニシャライザでは`return`を書く必要がありませんがFailable Initializerの場合は失敗したと判断した時点で`return nil`を実行させる必要があります。

```swift

init?(name: String) {
    if name.isEmpty {
        return nil
    }
}

```

### Designated Initializer

Designated Initializerは例えば以下のように書きます。

```swift

init() {
	// ...
}
init(name: String) {
   self.name = name
   super.init()
}

```

Designated Initializerは後述するDefault Initializer, Memberwise Initializerが暗黙的に定義されていない場合かならず1つは実装する必要があります。
例えば以下の様なコードはコンパイルエラーとなります。

```swift

class Sample {

	let name: String

}

```

また、Designated Initializerを追加すると、それに対して暗黙的に定義されていたDefault InitializerやMemberwise Initializerは利用できなくなります。
さらにDesignated Initializerはあるクラスを継承している場合、親クラスのDesignated Initializerを必ず呼ばなくてはなりません。

### Convenience Initializer

Convenience Initializer は以下のように`convenience`修飾子を付けて書きます。

```swift

convenience init() {
    /// ...
}

```

Convenience Initializerは同じクラス(親クラスのものでもない)のDesignated Initializerを呼び出す(移譲する)必要があり、Designated Initializerに対して補助的な役割を担います。
例えば以下のようにして実装します。

```swift

class Sample {

	let name: String

	init(name: String) {
		self.name = name
	}

	// nameを指定しなくても初期化できる補助的なイニシャライザ
	convenience init() {
		self.init(name: "Guest")
	}

}
```

### Required Initializer

サブクラスに必ず実装させたいイニシャライザです。
以下のようにして書きます。

```swift
required init(name: String)
```

required修飾子をつけたイニシャライザをオーバーライドする際はoverride修飾子は不要です。
例えば以下のようにして実装します。

```swift

class Sample {

	let name: String

	required init(name: String) {
		self.name = name
	}

}

class Example: Sample {

	required init(name :String) {
		super.init(name: name)
	}

}

```


### Default Initializer

イニシャライザでプロパティに値をセットする必要が無い場合に、Designated Initializerが実装されていない場合は以下のようにDefault Initializerが暗黙的に追加されます。

```swift

class Sample {

	// "Guest"が宣言時に代入されているためイニシャライザで代入する必要はない(むしろ定数であるためできない)
	let name: String = "Guest"
	
}

struct Example {

	// Optionalなのでイニシャライザで何も指定しなくともnilになる
	var name: String?

}

// Default Initializer を使って初期化
let sample = Sample()
let example = Example()

```

Designated Initializerを追加するとDefault Initializerは呼び出せなくなります。

### Memberwise Initializer

Structureに対してイニシャライザが実装されていない時に、イニシャライザで値の代入が必要なプロパティがある場合にそれらを引数にとったイニシャライザが暗黙的に追加されます。
これをMemberwise Initializerといいます。

```swift

struct Sample {

	let name: String = "Guest"
	let number: Int
	
}

let sample = Sample(number: 1)

```

Initializerを追加するとMemberwise Initializerは呼び出せなくなります。

### イニシャライザの継承について

Swiftにおいてデフォルトでは親クラスのイニシャライザは継承しません。
しかし、以下の条件に当てはまる場合は自動的に対応するイニシャライザが継承されます。

**Case 1** Designated Initializerが実装されていない場合はDesignated Initializer, Convenience Initializerがすべて継承される

**Case 2** 親クラスのすべてのDesignated Initializerをoverrideしている場合はすべてのConvenience Initializerが継承される

```swift:Case_1

//Case 1

class Sample {

    let title: String
    let body: String

    init(title: String?, body: String) {
        self.title = title ?? ""
        self.body = body
    }

    convenience init() {
        self.init(title: nil, body: "No description")
    }

}

class Example: Sample {
}

_ = Example(title: "Title", body: "Body")
_ = Example()

```


```swift:Case_2

//Case 2

class Sample {

    let title: String
    let body: String

    init(title: String?, body: String) {
        self.title = title ?? ""
        self.body = body
    }

    convenience init(body: String) {
        self.init(title: nil, body: body)
    }

}

class Example: Sample {

    override init(title: String?, body: String) {
        super.init(title: title, body: body)
    }

}

_ = Example(body: "No description")

```


### Classの初期化の順序について

Swiftでは大きく2つのフェーズで初期化を行います。

##### Phase 1

1. Designated Initializer または Convenience Initializerが呼ばれる
2. クラスに対するメモリが確保される(この段階ではまだメモリは初期化されていない)
3. Designated Initializerがプロパティに値をセットし、それに対するメモリを初期化します
4. Designated Initializerから親クラスのDesignated Initializerが呼ばれる(移譲)されます
5. 親クラスのチェーンを最後まで順々にたどってDesignated Initializerを呼んで処理を行っていきます
6. すべてのプロパティに値がセットされPhase 1完了となります

##### Phase 2

1. Designated Initializerの中で`self`を利用してメソッドをコールしたりすることができます(プロパティへのセットももちろん可能)
2. Convenience Initializerの中で`self`を利用してメソッドをコールしたりすることができます(プロパティへのセットももちろん可能)
3. `self`を利用したカスタマイズが完了したらPhase 2完了となります


つまり、Designated Initializer, Convenience Initializerそれぞれにおいて処理の順序は

##### Designated Initializer

1. 自身がもつプロパティへ値をセット
2. 親クラスのDesignated Initializerを呼び出す
3. `self`を使って必要であれば処理を行う

##### Convenience Initializer

1. 自身のDesignated Initializerを呼び出す
2. `self`を使って必要であれば処理を行う

となります。

Objective-Cでは誤って不適切なイニシャライザが呼ばれたり、プロパティの初期化が行われていないことが不具合につながることもあるかと思いますが、Swiftではこの辺りが安全な設計によりカバーされています。

# さいごに

走り書きっぽく書いてしまったので読みづらい箇所があるかもしれません :bow: 
記載内容に誤りがある場合はコメントでご指摘いただくか編集リクエストをいただけるととっても嬉しいです :smile: ♪