LoginSignup
14
13

More than 5 years have passed since last update.

[Swift]protocol extensionの罠

Posted at

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のメソッド呼び出しの仕組みをよく知らないのでわかんないですけど。
今回たまたま実行されていないのに気づいたけど、過去に同じようなことやってバグを作ってないか心配。
というわけで、みなさんもお気をつけて。

14
13
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
14
13