概要
Swift2.0 で追加された Protocol Extensions のメソッドディスパッチの振る舞いが、混乱しやすい物だと感じたので、その振る舞いについてまとめておきます。
Protocol Extensions の振る舞い
protocol SomeProtocol {
}
extension SomeProtocol {
func someMethod() {
print("call someMethod of SomeProtocol")
}
}
struct SomeStruct : SomeProtocol {
func someMethod() {
print("call someMethod of SomeStruct")
}
}
このような定義があるとき
let a = SomeStruct()
a.someMethod() // => "call someMethod of SomeStruct"
SomeStruct
型の変数a
のsomeMethod
呼び出しの結果、
SomeStruct
に定義されたsomeMethod
が呼び出されます。
これは多くの人が期待する振る舞いだと思います。
では、
let b:SomeProtocol = a
b.someMethod() // => ?
このようにSomeStruct
型の変数a
を代入したSomeProtocol
型の変数b
を宣言し、そのb
のsomeMethod
を呼び出すとなんと出力されるでしょうか。
少なくとも私は"call someMethod of SomeStruct"
と出力されることを期待していたのですが、実際は"call someMethod of SomeProtocol"
が出力されます。
つまり現状 Protocol Extensions のメソッド呼び出しは、polymorphic な振る舞いをしないようです。
Default Implementations
一方、Protocol の Default Implementations においては、上記とはメソッドディスパッチの振る舞いが異なります。
protocol SomeProtocol {
func someMethod()
}
extension SomeProtocol {
func someMethod() {
print("call someMethod of SomeProtocol")
}
}
struct SomeStruct : SomeProtocol {
func someMethod() {
print("call someMethod of SomeStruct")
}
}
let b:SomeProtocol = SomeStruct()
b.someMethod() // => "call someMethod of SomeStruct"
これらの挙動を知らずに、以下のようなコードを書くと、想像しているものとは異なる振る舞いに悩まされるのでご注意ください。
protocol SomeDelegate {
}
extension SomeDelegate {
func optionalMethod() {
// nothing to do.
}
}
class SomeClass: SomeDelegate {
func optionalMethod() {
print("optionalMethod") // ← 呼ばれない
}
}
let delegate: SomeDelegate = SomeClass()
delegate.optionalMethod()