LoginSignup
1
1

More than 3 years have passed since last update.

【Swift】protocol extensionしたクラスをクラス変数として使う方法

Last updated at Posted at 2019-08-06

はじめに

swiftのprotocol extensionを使う際に少々詰まったところがあったため覚え書きです。

具体的には、protocol extensionをクラスのフィールド変数として使う際、そのままでは機能しない場合があるという点をご説明します。
オブジェクト志向の初歩的な仕様であるかもしれませんが、ご参考になれば幸いです。

目次

この記事は以下のような構成です。

  • protocolの使い方
  • protocol extensionの使い方
  • protocol extensionをクラス変数として使う方法

protocolの使い方

まずはprotocolの基本文法です。最もシンプルには以下のようになるかと思います。

protocol MyProtocol {
    func hoge()
}

// protocolを継承したクラス
class MyClass: MyProtocol {
    func hoge() {
        print("My Class")
    }
}

let myClassInstance: MyClass = MyClass()
myClassInstance.hoge() // "My Class"

protocol extensionの使い方

続いて、protocol extensionの基本です。

protocol MyProtocol {
    func hoge()
}

// 拡張
extension MyProtocol {
    // hoge() を定義 (必要)
    func hoge() {
    }

    // hogehoge() を定義
    func hogehoge() {
        print("My extended Protocol")
    }
}

// extensionを継承したクラス
class MyClass: MyProtocol {
    // extension で定義済みの func hoge() は書かなくても良い。

    // hogehoge() を書き換え
    func hogehoge() {
        print("My Class")
    }
}

// protocol extensionをそのまま使う。
let MyProtocolInstance: MyProtocol = MyProtocol()
myMyProtocolInstance.hogehoge() // "My extended Protocol"

// protocol extensionを継承したクラスを使う。
let myClassInstance: MyClass = MyClass()
myClassInstance.hogehoge() // "My Class"

ここまでは問題ないかと思います。

protocol extensionを継承したクラスをクラス変数として使う方法

少しややこしいのは、上記の 「protocol extensionを継承したクラス」 をクラス変数として使う場合です。

// 以下では注意が必要。
class Test {
    var testData: MyProtocol?

    init() {
        testData = MyClass() // 継承後のクラスに変更したつもり...
        testData.hogehoge()
    }
}

// この場合はMyProtocol型オブジェクトになり、継承先のクラスにはならない。
let testInstance: Test = Test() // "My extended Protocol" となる。

上記のように init() において継承先のクラスを用いたつもりが、実際は継承前の protocol extensionが使われることになることが分かります。

この原因として、クラス変数 testData の型宣言時に継承前のものを宣言していることが関係しているようです。

protocol extensionを正しくクラス変数として使う

以下の方法で、継承先のクラスをクラス変数として使用できます。

具体的には、一旦protocol extensionを継承した 「親クラス」 に相当するものを用意し、実際に使うクラスとしてこれを継承した 「子クラス」 を用いることで正しく機能します。

protocol MyProtocol {
    func hoge()
}

extension MyProtocol {
    // extension では hoge() のみ定義してやることにする
    func hoge() {
    }
}

// extension を継承した 「親クラス」 を用意。
class MyParentClass: MyProtocol {
    // MyProtocolで定義済みの func hoge() は書かなくても良い。

    // 新関数 hogehoge() を定義
    func hogehoge() {
        print("My Parent Class")
    }
}

// 「親クラス」 を継承した 「子クラス」
class MyChildClass: MyParentClass {
    // override を記述した上で hogehoge() を書き換え。
    override func hogehoge() {
        print("My Child Class")
    }
}


// 上記 「子クラス」 であれば、クラスフィールドで使うことができる。
class Test {
    // MyProtocol ではなく、 MyParentClass 型で指定するのが重要。
    var testData: MyParentClass?

    init() {
        testData = MyChildClass() // MyChildClass 型に変更。
        testData.hogehoge()
    }
}

// 継承後の MyChildClass を動作させられている。
let testInstance: Test = Test() // "My Child Class"

終わりに

いかがでしたでしょうか?

ご参考になれば幸いです!
改善方法やご意見などあれば、どしどしコメント下さい!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1