メソッドディスパッチは、呼び出し時に適切なメソッドを選択するために使用されるアルゴリズムです。メソッドディスパッチの主な目的は、特定のメソッドの実行可能コードがメモリ内のどこにあるかという情報をプログラムに提供することです。
コンパイル言語には3種類のメソッドディスパッチがあります:
静的ディスパッチはSwiftで最も高速なディスパッチ方法です。メソッドのオーバーライドが利用できないため、メソッドの実装は1つだけであり、メモリ内の単一の場所に存在します。
\ static、final、privateなどのキーワードを使用して静的ディスパッチを利用できます。
\ 値型はオーバーライドできないため、静的ディスパッチは値型のデフォルトのメソッドディスパッチです。
\ いくつかの例を見てみましょう:
クラスにfinalキーワードを追加すると、そのメソッドはオーバーライドをサポートしなくなり、この時に静的ディスパッチが使用されます。
// MARK: Final class final class ClassExample { // MARK: Static dispatch func method() { // implementation ... } }
拡張を使用してプロトコルのデフォルト実装を追加すると、そのディスパッチ方法はWitness Tableを使用する代わりに静的ディスパッチに切り替わります。
// MARK: Prorocol Extension extension ProtocolExample { // MARK: Direct Dispatch func method() { // implementation ... } } class ClassExample2: ProtocolExample {} let classExample2 = ClassExample2() classExample2.method()
メソッドが拡張内で実装されている場合、サブクラスによってオーバーライドできないことを意味します。この場合、静的ディスパッチの余地があります。
// MARK: Example Class Extension class ClassExample3 {} extension ClassExample3 { // MARK: Direct Dispatch func method() { // implementation ... } } let classExample3 = ClassExample3() classExample3.method()
クラス本体の外部からプライベートメソッドにアクセスすることはできません。これは、メソッドがオーバーライドできず、静的ディスパッチを使用することを意味します。
// MARK: Access Control class ClassExample4 { // MARK: Direct Dispatch private func method() { // implementation ... } }
テーブルディスパッチは継承を扱う必要がある場合に使用されます。これはSwiftで使用されるデフォルトのディスパッチタイプです。
クラスまたはサブクラスのインスタンスごとに、各クラスに実装されたメソッドに関する情報を含み、適切な実装への参照を格納する仮想テーブルが作成されます。仮想テーブルディスパッチの主な欠点は、静的ディスパッチよりも速度が低いことです。
\ 例を見てみましょう:
// MARK: Virtual Table class ParentClass { func method1() {} func methdod2() {} } class ChildClass: ParentClass { override func method1() {} func method3() {} }
\ インスタンスごとに、独自の仮想テーブルが次のように作成されます:
\
Witnessテーブルはプロトコルによって使用され、プロトコルに準拠する各クラスに対して作成されます。CPUはこのテーブルを使用して、適切な実装を探すべき場所を決定します。プロトコルに準拠する各型(値型と参照型)は、プロトコルによって要求される型のメソッドへのポインタを含む独自のProtocol Witness Tableを持っています。
\ 例を見てみましょう:
// MARK: Witness Table Dispatch protocol ProtocolExample { func method1() func method2() } class ClassExample1: ProtocolExample { func method1() {} func method2() {} } class ClassExample2: ProtocolExample { func method1() {} func method2() {} }
\ この場合、各クラスに対してwitnessテーブルが作成されます:
\
メッセージディスパッチは最も動的なメソッドディスパッチスタイルです。実行時に適切な実装を探します。実行時に動作するため、Method Swizzlingを使用してメソッドの実装を変更することができます。
\ メッセージディスパッチを使用したい場合は、メソッド実装の前に@objc dynamicを追加する必要があります。
// MARK: Message Dispatch class ClassExample: NSObject { @objc dynamic func method() {} } class SubClassExample: ClassExample { @objc dynamic override func method() {} } let subclass = SubClassExample() subclass.method()
\ メソッドの実装はSubClassExample内で検索されます。そのクラスにこのメソッドの実装がない場合、検索は親クラスで続行され、NSObjectに到達するまで続きます。
\
すべての種類を1つの表にまとめてみましょう:
\
要約すると、Swiftにおけるメソッドディスパッチはコード実行の重要な側面であり、パフォーマンスと柔軟性に影響を与えます。適切なディスパッチ方法を選択することで、開発者はコードを最適化し、適応性を確保し、Swiftの動的機能を効果的に活用することができます。メソッドディスパッチを理解し習得することは、効率的で適応性のあるSwiftアプリケーションを構築するために不可欠です。

