アプリケーション共同開発部のしみずです。
このブログを読んでいるみなさんならSwift好きですよね?ということは、Protocolも好きですよね?
では、Protocolのメソッドがどのように呼び出されるのかご存知でしょうか?
今回は、Protocolに関して一度は見たことや嵌ったことがあるかもしれない事例と、その原因の考察をしてみたいと思います。
事例
最後の行では、S structのインスタンスを渡しているのにも関わらず、P protocolのmethod()が出力されています。
これの解決方法は、P protocolの定義本体に”func method()”を含めることですが、ではなぜ、P.method()が実行されるのでしょうか?
それはextensionメソッドは、Static Dispatchだからです。
Swiftで利用されるディスパッチの種類
ここでディスパッチについて整理します。
Swiftのディスパッチは、3つに分類できます。
Dynamic Dispatch
Objective-Cクラスでは基本的にこの方式が取られており、Cocoaの世界でよく目にするKVO, UIAppearance, Core Data, SwizzlingなどはこのDynamic Dispatchを駆使して実現されています。
Virtual Dispatch
多くのOOP言語はこの方式が利用されており、純粋なSwiftクラスでも利用されています。
他の言語では”Virtual Table”や”vtable”と呼ばれていますが、Swiftでは”Witness Table”という言い方をするようです。
Protocolの必須メソッドの呼び出しも基本的にこの方式が利用されます。
Static Dispatch
言わずもがなのことですが、「Struct」と「finalがついたクラスやメソッド」などはこの方式が利用されます。
Objective-Cクラス以外のextensionメソッドもこの方式になります。
Protocolのディスパッチの様子
では実際にProtocolの必須メソッドがどのように呼び出されているのかInstruments.appを使ってのぞいてみようと思います。
このProtocolに準拠した構造体、クラスとそのサブクラスのインスタンスを用意し、最初の事例と同じように型注釈にPを指定します。
これらのインスタンスに対してProtocolメソッドを読んだ結果が↓になります。
protocolMethod()
Protocolの定義には含まれないextensionメソッドは、最初の事例と同じようにPの実装が直接呼び出されます。
defaultImplementationMethod()
こちらはProtocol定義に含まれるため、Pの実装ではなくそれぞれのインスタンスの実装が呼び出されます。
method()
先ほどと同じくProtocol定義に含まれるため、構造体もクラスもそれぞれの実装したものが呼び出されています。
overriddenMethod()
こちらのメソッドは、Cで実装しSubCでオーバーライドした状態で、SubCのインスタンスから呼び出しています。
少しわかりにくいですが、”protocol witness for P.overriddenMethod() in conformance C“と書かれているようにスーパークラスCの準拠を辿っていますが、最終的に”SubC.overriddenMethod()”とあるようにSubC側の実装が呼び出されていることがわかります。
dynamicMethod()
“@objc dynamic func dynamicMethod()”
Cでこのように実装しています。
Protocolの”Virtual Dispatch”に続いて”Dynamic Dispatch”が利用されている様子が伺えます。
覚えておくこと
これでProtocolのメソッドがどのように呼び出されているか整理できましたでしょうか?
最初の事例に対応するために覚えておくことは…
- Protocolの必須メソッドは、Virtual Dispatch
- extensionメソッドは、StaticDispach
その他
extensionメソッドもVirtual Dispatchにする方法
ありません。
これを実現するためには、Objective-Cと同じようにDynamic Dispatchにする必要があるようです。
Protocol Witness Table
先ほどチラッと登場したProtocol Witness TableもABI安定化の対象となっているようです。
参考
あなたのメソッドはどこから? Protocol「私は…」
「Witness Tableから」
フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!
フェンリル採用チームの Twitter アカウントです。応募前のお問い合わせや、ちょっとした相談ごとなどお気軽にどうぞ!
フェンリルの Facebook ページでは、最新トピックをお知らせしています。よろしければいいね!してください!
Sleipnir の Facebook ページでは、ユーザーの方たちとのコミュニケーションや最新情報の投稿などを行なっています。よろしければいいね!してください!