[Swift] クラスのイニシャライザ３： Designated Initializer と Convenience Initializer

# はじめに

この記事では、designated initializer と convenience initializer について説明します。


# 他の記事へのリンク

* [[Swift] クラスのイニシャライザ１： イニシャライザの引数の書き方](http://qiita.com/cotrpepe/items/bfebe21e9f75c1fce60c)
* [[Swift] クラスのイニシャライザ２： プロパティの初期化方法](http://qiita.com/cotrpepe/items/db842f74b4c8ce16052c)
* [[Swift] クラスのイニシャライザ３： Designated Initializer と Convenience Initializer](http://qiita.com/cotrpepe/items/3052d91468f1b852582a)
* [[Swift] クラスのイニシャライザ４： override 修飾子](http://qiita.com/cotrpepe/items/8d197a9a288eb24c19fb)
* [[Swift] クラスのイニシャライザ５： required 修飾子](http://qiita.com/cotrpepe/items/3931c0c20ef43f4a18ac)
* [Swift] クラスのイニシャライザ６： Failable Initializer


# 環境

* Xcode 6.1 (Playground)


# 1. クラスのイニシャライザには２種類ある

クラスのイニシャライザには、以下の２種類があります。

* Designated initializer
* Convenience initializer


# 2. Designated Initializer とは？

## 2.1. 役割

基本となるイニシャライザです。


## 2.2. 書き方

```swift
init () {
	...
}
```


## 2.3. 必須か否か

クラスに１つ以上必要です。

[注）
詳細は後述](#pos1)


## 2.4. 他のイニシャライザの呼び出し

* スーパークラスの designated initializer を呼び出す

もしくは、

* 他のイニシャライザを一切呼び出さない

のいずれかです。

[注）
詳細は後述](#pos2)


## 2.5. 二段階の初期化処理

Designated initializer の中では、以下の二段階で初期化処理を実装する必要があります。

```swift
init() {
    フェーズ１、自分とスーパークラスのプロパティの初期化
    フェーズ２、自分とスーパークラスのプロパティのカスタマイズ
}
```

[注）
詳細は後述](#pos3)


# 3. Convenience Initializer とは？

## 3.1. 役割

Designated initializer の補助的な役割を果たします。


## 3.2. 書き方

```swift
convenience init() {
	...
}

```


## 3.3. 必須か否か

必須ではありません。


## 3.4. 他のイニシャライザの呼び出し

必ず同じクラスの designated initializer か convenience initializer を呼び出す必要があります。

[注）
詳細は後述](#pos4)


## 3.5. 二段階の初期化処理

Designated initializer と同様に、convenience initializer の中でも以下の二段階で初期化処理を実装する必要があります。

```swift
convenience init() {
    フェーズ１、自分とスーパークラスのプロパティの初期化
    フェーズ２、自分とスーパークラスのプロパティのカスタマイズ
}
```

[注）
詳細は後述](#pos3)


# <a name="pos1">4. Designated Initializer は必須

Designated initializer は、クラスに１つ以上実装する必要があります。実装しないとエラーが発生します。

```swift
//
// error: class 'Animal' has no initializers
//
class Animal {
    let name: String
}
```


## 4.1. 全てのプロパティがデフォルト値を持っている場合

ただし、全てのプロパティがデフォルト値を持っている場合は、明示的にイニシャライザを実装する必要はありません。暗黙的に designated initializer が実装されます。

このとき、暗黙的に実装されるイニシャライザのことを、「default initializer」と言います。

```swift
//
// 全てのプロパティにデフォルト値が設定されているので、
// 暗黙的に default initializer "init() {}" が実装される
//
class Animal {
    var name: String = "unknown"
}

//
// Default initializer "init()" を呼び出している
//
var a = Animal()
```


## 4.2. イニシャライザが自動的に継承される場合

サブクラスの場合はイニシャライザが自動的に継承される場合があります（[継承される条件は後述](#pos5)）。その場合、明示的にイニシャライザを実装する必要はありません。

以下の例では、designated initializer  がスーパークラスから継承されています。

```swift
class Animal {
    let name: String
    
    init(name: String) {
        self.name = name
    }
}

//
// スーパークラス Animal から "init(name: String)" が継承される
//
class Cat: Animal {}

//
// スーパークラス Animal から継承された "init(name: String)" を呼び出している
//
var c = Cat(name: "kitty")
```


# <a name ="pos2">5. Designated Initializer はスーパークラスの Designated Initializer を呼び出すことができる

Designated initializer はスーパークラスの designated initializer を呼び出すことができます。

![d2super_d.png](https://qiita-image-store.s3.amazonaws.com/0/46434/5fcb51f9-cff4-ce31-eae2-d2103f943de7.png)

```swift
class Animal {
    let name: String

    //
    // 呼び出される側
    //    
    init(name: String) {
        self.name = name
    }
}

class Cat: Animal {
    //
    // 呼び出す側
    //
    init() {
        super.init(name: "kitty")
    }
}

var c = Cat()
println(c.name) // -> kitty
```

しかし、スーパクラスの convenience initializer を呼び出すことはできません。

![d2super_c.png](https://qiita-image-store.s3.amazonaws.com/0/46434/2d6c7c7d-285f-e815-cddc-8785d0f1af10.png)

```swift
class Animal {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    
    //
    // 呼び出される側
    //
    convenience init() {
        self.init(name: "unknown")
    }
}

class Cat: Animal {
    //
    // 呼び出す側
    //
    init() {
        super.init() // -> error: must call a designated initializer of the superclass 'Animal'

    }
}
```

また、同じクラスのイニシャライザを呼び出すこともできません。

![d2self_d.png](https://qiita-image-store.s3.amazonaws.com/0/46434/4a121ab3-cc58-b9dd-411d-3448634ecb33.png)

```swift
class Animal {
    let name: String
    
    //
    // 呼び出される側
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // 呼び出す側
    //
    init() {
        self.init(name: "unknown") // -> error: designated initializer for 'Animal' cannot delegate (with 'self.init'); did you mean this to be a convenience initializer?
    }
}
```

![d2self_c.png](https://qiita-image-store.s3.amazonaws.com/0/46434/b87a8d8f-b9a1-a288-e5e3-dae59681aedc.png)

```swift
class Animal {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    
    //
    // 呼び出される側
    //
    convenience init() {
        self.init(name: "unknown")
    }
    
    //
    // 呼び出す側
    //
    init(nickname: String) {
        self.init() // -> error: designated initializer for 'Animal' cannot delegate (with 'self.init'); did you mean this to be a convenience initializer?
    }
}
```

他のイニシャライザを呼び出さなくても問題はありません。

![notCall_d.png](https://qiita-image-store.s3.amazonaws.com/0/46434/a4074b12-7627-e217-5419-4153c5deac06.png)

```swift
class Animal {
    let name: String
    
    init() {
        self.name = "unknown"
    }
}
```


# <a name ="pos4">6. Convenience Initializer は同じクラスのイニシャライザを呼び出すことができる

Convenience initializer は同じクラスのイニシャライザを呼び出すことができます。

![c2self_d.png](https://qiita-image-store.s3.amazonaws.com/0/46434/a82de6eb-81dd-6dd7-4420-d75821929d96.png)

```swift
class Animal {
    let name: String
    
    //
    // 呼び出される側
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // 呼び出す側
    //
    convenience init() {
        self.init(name: "unknown")
    }
}

var a = Animal()
println(a.name) // -> unknown
```

![c2self_c.png](https://qiita-image-store.s3.amazonaws.com/0/46434/9772698e-1ca0-95be-4013-dd37a695163b.png)

```
class Animal {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    
    //
    // 呼び出される側
    //
    convenience init(nickname: String) {
        self.init(name: nickname)
    }
    
    //
    // 呼び出す側
    //
    convenience init() {
        self.init(nickname: "kitty")
    }
}

var a = Animal()
println(a.name) // -> kitty
```

同じクラスのイニシャライザを呼び出さないとエラーが発生します。

![notCall.png](https://qiita-image-store.s3.amazonaws.com/0/46434/bd209c79-baec-5778-4de5-8793f30ce70b.png)

```swift
class Animal {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    
    //
    // error: self.init isn't called on all paths in delegating initializer
    //
    convenience init() {
    }
}
```

また、designated initializer と違い、スーパークラスのイニシャライザを呼び出すことはできません。

![c2super_d.png](https://qiita-image-store.s3.amazonaws.com/0/46434/f14354db-6467-d27c-12c5-3e56d56a39d2.png)

```swift
class Animal {
    let name: String
    
    //
    // 呼び出される側
    //
    init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    //
    // 呼び出す側
    //
    convenience init(name: String) {
        super.init() // -> error: Convenience initializer for 'Cat' must delegate (with 'self.init') rather than chaining to a superclass initializer (with 'super.init')
    }
}
```

![c2super_c.png](https://qiita-image-store.s3.amazonaws.com/0/46434/3cfce0f0-c8e3-b9fa-2489-290cf33b3ed1.png)

```swift
class Animal {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    
    //
    // 呼び出される側
    //
    convenience init() {
        self.init(name: "unknown")
    }
}

class Cat: Animal {
    //
    // 呼び出す側
    //
    convenience init(nickname: String) {
        super.init() // -> error: must call a designated initializer of the superclass 'Animal'
    }
}
```

# 7. Convenience Initializer は、最終的に Designated Initializer を呼び出さなければならない

Convenience initializer は、最終的に designated initializer を呼び出さなければいけません。これを守らないと、無限ループが発生してしまいます。

Convenience initializer は、必ず他のイニシャライザを呼ぶ必要があります。そのため、convenience initializer が convenience initializer だけを呼んでいると、結果として無限ループが発生してしまいます。

Designated initializer の場合は、「他のイニシャライザを一切呼び出さない」ということが可能なので、最終的に designated initializer を呼び出せば、無限ループに陥ることはありません。

注）
最終的に designated initializer を呼び出さなくても、コンパイラはエラーを出力しないので注意が必要です。

![final_d.png](https://qiita-image-store.s3.amazonaws.com/0/46434/dc6b4408-ba29-bf0c-2945-0a087f22d84a.png)

![finalNot_d.png](https://qiita-image-store.s3.amazonaws.com/0/46434/f5acd7d1-4b47-a3f8-f771-cbef715d5b95.png)

```swift
class Animal {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    
    //
    // A
    //
    convenience init() {
        //
        // B を呼び出す
        //
        self.init(nickname: "unknown")
    }
    
    //
    // B
    //
    convenience init(nickname: String) {
        //
        // A を呼び出す
        //
        self.init()
    }
}

//
// A を呼び出す
//
var a = Animal() // -> 無限ループに陥る
```


# <a name ="pos3">8. 二段階の初期化処理

イニシャライザ内では、以下の二段階の初期化処理を正しい順番で実装する必要があります。

* フェーズ１ ： 自分とスーパークラスのプロパティの初期化
* フェーズ２ ： 自分とスーパークラスのプロパティのカスタマイズ

これは、プロパティが意図しない形で上書きされてしまうことを防ぐためのルールです。


## 8.1. プロパティが意図せず上書きされてしまう例

例えば、以下のコードでは、スーパークラスの初期化が完了する前にスーパークラスをカスタマイズしているので、意図したとおりに値が設定できません。

```swift
class Animal {
    var name: String
    
    init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    init(name: String) {
        //
        // スーパークラスのプロパティのカスタマイズ
        //
        self.name = name

        //
        // スーパークラスのプロパティの初期化
        //
        super.init()
    }
}

var c = Cat(name: "kitty")
println(c.name) // -> unknown （実際にはコンパイラエラーになり、コンパイルできない。イニシャライザ内では、正しい順番で処理を実装しないとコンパイラエラーが発生する。詳細は後述）
```

以下の順番に直せば、意図どおりにプロパティが設定されます

```swift
class Animal {
    var name: String
    
    init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    init(name: String) {
        //
        // スーパークラスのプロパティの初期化
        //
        super.init()

        //
        // スーパークラスのプロパティのカスタマイズ
        //
        self.name = name
    }
}

var c = Cat(name: "kitty")
println(c.name) // -> kitty
```

上記のように、イニシャライザの中では初期化処理を正しい順番で実行しないと、プロパティが意図しない形で上書きされてしまいます。

そのため、swift では「二段階の初期化処理」というものを定義し、イニシャライザ内の処理の順番を厳格に定めています。


## 8.2. 二段階の初期化処理の具体的な中身

二段階の初期化処理を、より細かい粒度で見ると以下のようになります。

注）
Swift では、以下の正しい順番が破られていた場合に、コンパイラがエラーを出力してくれるようになっています。そのため、この順番を覚える必要はありません。

### 8.2.1. Designated Initializer の場合

```swift
init() {
    ///////////////////////////////////////////////////////////
    // フェーズ１、自分とスーパークラスのプロパティの初期化
    ///////////////////////////////////////////////////////////
    
    //
    // １、自分のプロパティの初期化
    //
    self.property_of_self = "Initialization"
    
    //
    // ２、スーパークラスのイニシャライザの呼び出し
    //
    super.init()
  
    
    ///////////////////////////////////////////////////////////
    // フェーズ２、自分とスーパークラスのプロパティのカスタマイズ
    ///////////////////////////////////////////////////////////
    
    //
    // ３、自分とスーパークラスのプロパティのカスタマイズ or インスタンスメソッドの呼び出し
    //
    self.property_of_self  = "Customization"
    self.property_of_super = "Customization"
    self.instance_method_of_self()
    self.instance_method_of_super()
}
```

#### 注意） プロパティの「初期化」と「カスタマイズ」の違い

１番目がプロパティの「初期化」で、

```swift:上記のコードを一部抜粋の上、再掲
    //
    // １、自分のプロパティの初期化
    //
    self.property_of_self = "Initialization"
```

３番目がプロパティの「カスタマイズ」であることに注意してください。

```swift:上記のコードを一部抜粋の上、再掲
    //
    // ３、自分とスーパークラスのプロパティのカスタマイズ or インスタンスメソッドの呼び出し
    //
    self.property_of_self  = "Customization"
    self.property_of_super = "Customization"
```

例えば、以下のコードでは定数 nickname を正常に初期化することができます。

```swift
class Animal {
}

class Cat: Animal {
    let nickname: String
    
    override init() {
        ///////////////////////////////////////////////////////////
        // フェーズ１、自分とスーパークラスのプロパティの初期化
        ///////////////////////////////////////////////////////////
        
        //
        // １、自分のプロパティの初期化
        //
        self.nickname = "Initialization"
        
        //
        // ２、スーパークラスのイニシャライザの呼び出し
        //
        super.init()
        
        ///////////////////////////////////////////////////////////
        // フェーズ２、自分とスーパークラスのプロパティのカスタマイズ
        ///////////////////////////////////////////////////////////      

        //
        // ３、自分とスーパークラスのプロパティのカスタマイズ or インスタンスメソッドの呼び出し
        //
    }
}
```

しかし、```self.nickname = "Initialization"``` を１番目から３番目に移動しただけの以下のコードでは、定数 nickname を初期化することができません。

```swift
class Cat: Animal {
    let nickname: String

    override init() {
        ///////////////////////////////////////////////////////////
        // フェーズ１、自分とスーパークラスのプロパティの初期化
        ///////////////////////////////////////////////////////////
 
        //
        // １、自分のプロパティの初期化
        //
        
        //
        // ２、スーパークラスのイニシャライザの呼び出し
        //
        super.init() // -> error: property 'self.nickname' not initialized at super.init call

        ///////////////////////////////////////////////////////////
        // フェーズ２、自分とスーパークラスのプロパティのカスタマイズ
        ///////////////////////////////////////////////////////////
  
        //
        // ３、自分とスーパークラスのプロパティのカスタマイズ or インスタンスメソッドの呼び出し
        //
        self.nickname = "Initialization"

    }
}
```

これは、１番目がプロパティの「初期化」をする場所で、３番目がプロパティの「カスタマイズ」をする場所だからです。１番目でプロパティが初期化されていないと、エラーが発生してしまいます。

イニシャライザ内の処理を実装する際は、プロパティの「初期化」と「カスタマイズ」の違いに注意してください。


### 8.2.2. Convenience Initializer の場合

```swift
convenience init() {
    ///////////////////////////////////////////////////////////
    // フェーズ１、自分とスーパークラスのプロパティの初期化
    ///////////////////////////////////////////////////////////
    
    //
    // １、同じクラスの他のイニシャライザの呼び出し
    //
    self.init()
    
    
    ///////////////////////////////////////////////////////////
    // フェーズ２、自分とスーパークラスのプロパティのカスタマイズ
    ///////////////////////////////////////////////////////////
    
    //
    // ２、自分とスーパークラスのプロパティのカスタマイズ or インスタンスメソッドの呼び出し
    //
    self.property_of_self  = "Customization"
    self.property_of_super = "Customization"
    self.instance_method_of_self()
    self.instance_method_of_super()
}
```

#### 注意） Convenience Initializer の中ではプロパティを初期化できない

２番目で行うのがプロパティの「初期化」ではなく、「カスタマイズ」であることに注意してください。

```swift:上記のコードを一部抜粋の上、再掲
    //
    // ２、自分とスーパークラスのプロパティのカスタマイズ or インスタンスメソッドの呼び出し
    //
    self.property_of_self  = "Customization"
    self.property_of_super = "Customization"
```

そのため、以下のコードのとおり、convenience initializer ではプロパティの「初期化」ができません。

```swift
class Animal {
    let name: String

    init() {
    }
    
    convenience init(name: String) {
        ///////////////////////////////////////////////////////////
        // フェーズ１、自分とスーパークラスのプロパティの初期化
        ///////////////////////////////////////////////////////////
    
        //
        // １、同じクラスの他のイニシャライザの呼び出し
        //
        self.init()
    
    
        ///////////////////////////////////////////////////////////
        // フェーズ２、自分とスーパークラスのプロパティのカスタマイズ
        ///////////////////////////////////////////////////////////
    
        //
        // ２、自分とスーパークラスのプロパティのカスタマイズ or インスタンスメソッドの呼び出し
        //
        self.name = "Initialization" // -> error: cannot assign to 'name' in 'self'

    }
}
```

プロパティを初期化する場合は、「プロパティの宣言時にデフォルト値を設定する」か、

```swift
class Animal {
    let name: String = "Initialization"
}
```

「Designated initializer の１番目の箇所で初期化する」のいずれかで初期化してください。

```swift
class Animal {
    let name: String

    init() {
        ///////////////////////////////////////////////////////////
        // フェーズ１、自分とスーパークラスのプロパティの初期化
        ///////////////////////////////////////////////////////////

        //
        // １、自分のプロパティの初期化
        //
        self.name = "Initialization"

        ...
    }
}
```


# <a name="pos5">9. イニシャライザの自動的な継承

以下のルール１、もしくは、ルール２の条件を満たすと、スーパークラスのイニシャライザがサブクラスに自動的に継承されます。


## 9.1. Designated Initializer の自動的な継承

***ルール１）***

***サブクラスが designated initializer を１つも実装していなかった場合、スーパークラスの全ての designated initializer が自動的に継承されます。***


### 9.1.1. 例） サブクラスが Designated Initializer を１つも実装していなかった場合

サブクラスが designated initializer を１つも実装していなかった場合、

![DNoInit1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/7b34ebb0-ebe7-15e5-f1b7-75594277364e.png)

スーパークラスの全ての designated initializer が自動的に継承されます。

![DNoInit2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/aa35fb80-8aa7-b612-c77a-1e166df9240d.png)

```swift
class Animal {
    let name: String
 
    //
    // A
    //
    init(name: String) {
        self.name = name
    }

    //
    // B
    //
    init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
}

//
// A を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B を呼び出す
//
var c2 = Cat() // -> OK
```


### 9.1.2. 例） サブクラスが Convenience Initializer だけを実装していた場合

サブクラスが convenience initializer だけを実装していた場合、

![DCInit1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/f70b7e0c-2be0-502c-029f-ab38478255d0.png)

Designated initializer が１つも実装されていない状態なので、スーパークラスの全ての designated initializer が自動的に継承されます。

![DCInit2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/10fa5308-05bf-9fe1-6dd3-27f22a2c6e2f.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    //
    // Y
    //
    convenience init(firstName: String, lastName: String) {
        //
        // スーパークラスから自動的に継承された A を呼び出している
        //
        self.init(name: firstName + " " + lastName)
    }
}

//
// A を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B を呼び出す
//
var c2 = Cat() // -> OK

//
// Y を呼び出す
//
var c3 = Cat(firstName: "John", lastName: "Smith") // -> OK
```


### 9.1.3. 例） サブクラスの Convenience Initializer がスーパークラスの Designated Initializer の一部をオーバーライドしていた場合

サブクラスの Convenience Initializer がスーパークラスの Designated Initializer の一部をオーバーライドしていた場合、

![DCOverride1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/3cd0f9a4-1cd0-37ae-8156-c0a2c4603575.png)

Designated initializer が１つも実装されていない状態なので、スーパークラスの全ての designated initializer が自動的に継承されます。

![DCOverride2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/c37ccaf9-cff9-b3de-3dc2-f2977045c657.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    //
    // A'
    //
    override convenience init(name: String) {
        //
        // スーパークラスから自動的に継承された B を呼び出している
        //
        self.init()
    }
}

//
// A' を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B を呼び出す
//
var c2 = Cat() // -> OK
```


### 9.1.4. 例） サブクラスの Designated Initializer が、スーパークラスの Designated Initializer を一部だけオーバーライドしていた場合

サブクラスの designated initializer が、スーパークラスの designated initializer を一部だけオーバーライドしていた場合、

![DOnlyOneOverride1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/d6ad65f1-9810-df4b-ea7a-a61d4414c35e.png)

サブクラスに designated initializer が実装されてしまっている状態なので、スーパークラスの designated initializer は自動的に継承されません。

![DOnlyOneOverride2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/aebadb5f-7cdc-d986-8042-4b6dd3691353.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    //
    // A'
    //
    override init(name: String) {
        super.init(name: name)
    }
}

//
// A' を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B を呼び出す
//
var c2 = Cat() // -> error
```


### 9.1.5. 例） サブクラスが、スーパークラスの Designated Initializer とは別の Designated Initializer を実装していた場合

サブクラスが、スーパークラスの designated initializer とは別の designated initializer を実装していた場合、

![DOtherInit1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/6c1a28a3-347c-812c-ae4f-ffe117fc7a95.png)

サブクラスに designated initializer が実装されてしまっている状態なので、スーパークラスの designated initializer は自動的に継承されません。

![DOtherInit2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/62b33451-a6c9-49fb-4773-10919f2050cb.png)

```swift
class Animal {
    let name: String
 
    //
    // A
    //
    init(name: String) {
        self.name = name
    }

    //
    // B
    //
    init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    //
    // X
    //
    init(firstName: String, lastName: String) {
        super.init(name: firstName + " " + lastName)
    }
}

//
// A を呼び出す
//
var c1 = Cat(name: "kitty") // -> error

//
// B を呼び出す
//
var c2 = Cat() // ->  error

//
// X を呼び出す
//
var c3 = Cat(firstName: "John", lastName: "Smith") // -> OK
```


## 9.2. Convenience Initializer の自動的な継承

***ルール２）***

***サブクラスが、スーパークラスの designated initializer を全て実装していた場合（自動的に継承された場合も含む）、convenience initializer も自動的に継承されます。***


### 9.2.1. 例） サブクラスがイニシャライザを１つも実装していなかった場合

サブクラスがイニシャライザを１つも実装していなかった場合、

![CNoInit1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/df753b49-a085-dfbf-0605-d098e6e83d7c.png)

Designated initializer が１つも実装されていない状態なので、スーパークラスの全ての designated initializer が自動的に継承されます（ルール１）。

さらに、サブクラスがスーパークラスの全ての designated initializer を実装している状態なので、convenience initializer も自動的に継承されます（ルール２）。

![CNotInit2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/1bf96e3b-0708-6ccc-2fdf-4cf90ae7b29b.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
    
    //
    // C
    //
    convenience init(firstName: String, lastName: String) {
        self.init(name: firstName + " " + lastName)
    }
}

class Cat: Animal {
}

//
// A を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B を呼び出す
//
var c2 = Cat() // -> OK

//
// C を呼び出す
//
var c3 = Cat(firstName: "John", lastName: "Smith") // -> OK
```


### 9.2.2. 例） サブクラスが、スーパークラスとは別の Convenience Initializer を実装していた場合

サブクラスが、スーパークラスとは別の convenience initializer を実装していた場合、

![COtherC1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/09dbe438-ef3b-b221-0e04-cb119c8af8e3.png)

Designated initializer が１つも実装されていない状態なので、スーパークラスの全ての designated initializer が自動的に継承されます（ルール１）。

さらに、サブクラスがスーパークラスの全ての designated initializer を実装している状態なので、convenience initializer も自動的に継承されます（ルール２）。

![COtherC2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/af98192c-b6f0-6717-1363-8cf4fab16e65.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
    
    //
    // C
    //
    convenience init(firstName: String, lastName: String) {
        self.init(name: firstName + " " + lastName)
    }
}

class Cat: Animal {
    //
    // Y
    //
    convenience init(nickname: String) {
        //
        // スーパークラスから自動的に継承された A を呼び出す
        //
        self.init(name: nickname)
    }
}

//
// A を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B を呼び出す
//
var c2 = Cat() // -> OK

//
// C を呼び出す
//
var c3 = Cat(firstName: "John", lastName: "Smith") // -> OK

//
// Y を呼び出す
//
var c4 = Cat(nickname: "kitty") // -> OK
```


### 9.2.3. 例） サブクラスの Designated Initializer が、スーパークラスの全ての Designated Initializer をオーバーライドしていた場合

サブクラスの designated initializer が、スーパークラスの全ての designated initializer をオーバーライドしていた場合、

![CAllOverride1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/1621da15-9b7d-b6ff-dabf-f0d02883bd5a.png)

サブクラスがスーパークラスの全ての designated initializer を実装している状態なので、convenience initializer も自動的に継承されます（ルール２）。

![CAllOverride2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/25f998b4-2756-8d32-fe5a-7e18b2246a52.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
    
    //
    // C
    //
    convenience init(firstName: String, lastName: String) {
        self.init(name: firstName + " " + lastName)
    }
}

class Cat: Animal {
    //
    // A'
    //
    override init(name: String) {
        super.init(name: name)
    }
    
    //
    // B'
    //
    override init() {
        super.init()
    }
}

//
// A' を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B' を呼び出す
//
var c2 = Cat() // -> OK

//
// C を呼び出す
//
var c3 = Cat(firstName: "John", lastName: "Smith") // -> OK
```


### 9.2.4. 例） サブクラスの Convenience Initializer がスーパークラスの Designated Initializer の一部をオーバーライドしていた場合

サブクラスの Convenience Initializer がスーパークラスの Designated Initializer の一部をオーバーライドしていた場合、

![CCOverride1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/80c00592-fb4e-8e4d-c189-d7c8f783285b.png)

Designated initializer が１つも実装されていない状態なので、スーパークラスの全ての designated initializer が自動的に継承されます（ルール１）。

さらに、サブクラスがスーパークラスの全ての designated initializer を実装している状態なので、convenience initializer も自動的に継承されます（ルール２）。

![CCOverride2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/7c1bee8f-1b60-d1a0-724f-0b3ed5490d24.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
    
    //
    // C
    //
    convenience init(firstName: String, lastName: String) {
        self.init(name: firstName + " " + lastName)
    }
}

class Cat: Animal {
    //
    // A'
    //
    override convenience init(name: String) {
        //
        // スーパークラスから自動的に継承された B を呼び出している
        //
        self.init()
    }
}

//
// A' を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B を呼び出す
//
var c2 = Cat() // -> OK

//
// C を呼び出す
//
var c3 = Cat(firstName: "John", lastName: "Smith") // -> OK
```


### 9.2.5. 例） サブクラスが、スーパークラスの Designated Initializer を一部しかオーバーライドしていなかった場合

サブクラスが、スーパークラスの designated initializer を一部しかオーバーライドしていなかった場合、

![COnlyOneOverride1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/0f71fc03-b820-44d3-9bf6-587eb9ec9bc8.png)

サブクラスに designated initializer が実装されてしまっている状態なので、スーパークラスの designated initializer は自動的に継承されません（ルール１）。

さらに、サブクラスがスーパークラスの全ての designated initializer を実装していないので、convenience initializer も自動的に継承されません（ルール２）。

![COnlyOneOverride2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/522158fe-1168-6b9b-4b04-51c664c44e1c.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
    
    //
    // C
    //
    convenience init(firstName: String, lastName: String) {
        self.init(name: firstName + " " + lastName)
    }
}

class Cat: Animal {
    //
    // A'
    //
    override init(name: String) {
        super.init(name: name)
    }
}

//
// A' を呼び出す
//
var c1 = Cat(name: "kitty") // -> OK

//
// B を呼び出す
//
var c2 = Cat() // -> error

//
// C を呼び出す
//
var c3 = Cat(firstName: "John", lastName: "Smith") // -> error
```


### 9.2.6. 例） サブクラスが、スーパークラスとは別の Designated Initializer を実装していた場合

サブクラスが、スーパークラスとは別の designated initializer を実装していた場合、

![COtherD1.png](https://qiita-image-store.s3.amazonaws.com/0/46434/a479d25d-2ae7-6754-bd4e-b74d6ebf3352.png)

サブクラスに designated initializer が実装されてしまっている状態なので、スーパークラスの designated initializer は自動的に継承されません（ルール１）。

さらに、サブクラスがスーパークラスの全ての designated initializer を実装していないので、convenience initializer も自動的に継承されません（ルール２）。

![COtherD2.png](https://qiita-image-store.s3.amazonaws.com/0/46434/f3ca81d8-932b-e364-e3d6-5e88a00717da.png)

```swift
class Animal {
    let name: String
    
    //
    // A
    //
    init(name: String) {
        self.name = name
    }
    
    //
    // B
    //
    init() {
        self.name = "unknown"
    }
    
    //
    // C
    //
    convenience init(firstName: String, lastName: String) {
        self.init(name: firstName + " " + lastName)
    }
}

class Cat: Animal {
    //
    // X
    //
    init(firstName: String, middleName: String, lastName: String) {
        super.init(name: firstName + " " + middleName + " " + lastName)
    }
}

//
// A を呼び出す
//
var c1 = Cat(name: "kitty") // -> error

//
// B を呼び出す
//
var c2 = Cat() // ->  error

//
// C を呼び出す
//
var c3 = Cat(firstName: "John", lastName: "Smith") // -> error

//
// X を呼び出す
var c4 = Cat(firstName: "John", middleName: "T.", lastName: "Smith") // -> OK
```


# 参考文献

[The Swift Programming Language](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_306)