Help us understand the problem. What is going on with this article?

[Swift]protocol extensionの罠

More than 1 year has passed since last update.

Swiftのプロトコルは便利なのでよく使うのですが、今になってハマったところがあったので。

optionalの代わりにextensionを使う

SwiftのプロトコルではObjective-Cにあったoptionalが廃止されました。
まあ、Objective-Cのoptionalはコンパイラがエラー吐かなくなるだけで、呼び出す際に毎回呼び出せるかチェックが必要だったのでかなり使い勝手悪かったですけど。
でも、Swiftでoptionalのような使い方ができないわけではなく、extensionでデフォルト動作を実装することで、optionalと同じような使い方ができます。

protocol SomeProtocol{
    // required method
    func r()

    // optional method
    func o()
}

extension SomeProtocol {
    // optional method
    func o() {
        print("o() in SomeProtocol")
    }
}

class SomeClass: SomeProtocol {
    func r() {
        print("r() in SomeClass")
    }
}

let sc = SomeClass()
sc.r() // 出力 "r() in SomeClass"
sc.o() // 出力 "o() in SomeProtocol"

ここまでは問題ありません。
問題は、僕が"extensionにoptionalにしたい関数を書けばいい"と覚えていたことです。

protocolにメソッド定義を書かない

そんなふうに覚えていた僕は下のようにプロトコルを定義したんです。

protocol SomeProtocol{
    // required method
    func r()
}

extension SomeProtocol {
    // optional method
    func o() {
        print("o() in SomeProtocol")
    }
}

プロトコル定義からメソッドo()の定義が抜けています。
これでもコンパイルは通ります。
けど、動作が少し違うんです。
下のコードみたいな感じで。

protocol SomeProtocol{
    // required method
    func r()

    // optional method
    func o()
}

extension SomeProtocol {
    // optional methods
    func o() {
        print("o() in SomeProtocol")
    }
    func p() {
        print("p() in SomeProtocol")
    }
}

class SomeClass: SomeProtocol {
    func r() {
        print("r() in SomeClass")
    }
    func o() {
        print("o() in SomeClass")
    }
    func p() {
        print("p() in SomeClass")
    }
}

let sc: SomeClass = SomeClass()
sc.r() // 出力 "r() in SomeClass"
sc.o() // 出力 "o() in SomeClass"
sc.p() // 出力 "p() in SomeClass"
let sp: SomeProtocol = sc
sp.r() // 出力 "r() in SomeClass"
sp.o() // 出力 "o() in SomeClass"
sp.p() // 出力 "p() in SomeProtocol"

protocolとextension両方にメソッド定義してあるo()は常にクラスのメソッドが実行されるのに対し、
extensionにしか定義してないメソッドp()は、変数の型によって呼び出されるメソッドが違います。

同名の呼び出せるメソッドが複数ある時の優先順位が変わるんですかね。
Swiftのメソッド呼び出しの仕組みをよく知らないのでわかんないですけど。
今回たまたま実行されていないのに気づいたけど、過去に同じようなことやってバグを作ってないか心配。
というわけで、みなさんもお気をつけて。

Hiroki_Kawakami
プログラム好きな大学生 最近はLinuxやラズパイに夢中で、iPhoneアプリ開発が疎かになってる
https://ideal-reality.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away