サブクラスで実装されたメソッドの代わりにSwiftプロトコル拡張メソッドが呼び出されます質問する

サブクラスで実装されたメソッドの代わりにSwiftプロトコル拡張メソッドが呼び出されます質問する

以下のコードで説明されている問題が発生しました (Swift 3.1)。

protocol MyProtocol {
    func methodA()
    func methodB()
}

extension MyProtocol {
    func methodA() {
        print("Default methodA")
    }

    func methodB() {
        methodA()
    }
}

// Test 1
class BaseClass: MyProtocol {

}

class SubClass: BaseClass {
    func methodA() {
        print("SubClass methodA")
    }
}


let object1 = SubClass()
object1.methodB()
//

// Test 2
class JustClass: MyProtocol {
    func methodA() {
        print("JustClass methodA")
    }
}

let object2 = JustClass()
object2.methodB()
//
// Output
// Default methodA
// JustClass methodA

だから私は「サブクラスメソッドA」呼び出し後にテキストが印刷されるはずですobject1.methodB()。しかし、何らかの理由で、methodA()プロトコル拡張からのデフォルトの実装が呼び出されます。ただし、object2.methodB()呼び出しは期待どおりに動作します。

これはプロトコル メソッド ディスパッチにおける別の Swift のバグでしょうか、それとも何かが欠けていてコードは正しく動作しているのでしょうか?

ベストアンサー1

これは、プロトコルが現在メソッドをディスパッチする方法です。

プロトコル証人テーブル(このWWDCトーク詳細については、プロトコル型インスタンスで呼び出されたときに、プロトコル要件の実装に動的にディスパッチするために使用されます。これは、特定の準拠型のプロトコルの各要件に対して呼び出す関数実装のリストにすぎません。

プロトコルへの適合を宣言する各タイプは、独自のプロトコル証人テーブルを取得します。ここで「適合を宣言する」と述べており、「に準拠する」だけではないことに注意します。は、適合BaseClassに関する独自のプロトコル証人テーブルを取得しますMyProtocol。ただし、SubClassないは への準拠に関する独自のテーブルを取得しますMyProtocolが、代わりに は の だけに依存しますBaseClass
: MyProtocolを の定義まで下げるとSubClass、 は独自の PWT を持つようになります。

したがって、ここで考えなければならないのは、 の PWT がどのBaseClassようなものかということです。 は、プロトコル要件のいずれの実装も提供していないmethodA()ためmethodB()、プロトコル拡張の実装に依存しています。つまり、BaseClassに準拠するPWT にはMyProtocol、拡張メソッドへのマッピングのみが含まれているということです。

したがって、拡張methodB()メソッドが呼び出され、 が を呼び出すとmethodA()、その呼び出しは PWT を通じて動的にディスパッチされます (プロトコル型インスタンス、つまり で呼び出されるためself)。したがって、これがSubClassインスタンスで発生すると、 の PWT が処理されます。したがって、 が の実装を提供しているという事実に関係なく、BaseClassの拡張実装が呼び出されることになります。methodA()SubClass

さて、 の PWT を考えてみましょうJustClass。 は の実装を提供するので、へmethodA()の適合性に関する PWT はMyProtocolそれのマッピングとしての実装methodA()、および の拡張実装methodB()。したがって、methodA()が PWT を介して動的にディスパッチされる場合、次のようになります。その実装。

私が言うようにこのQ&Aでサブクラスがスーパークラスが準拠するプロトコルに対して独自のPWTを取得しないというこの動作は確かに驚くべきものであり、バグとして報告SwiftチームのメンバーであるJordan Roseがバグレポートのコメントで述べているように、その理由は

[...] サブクラスは、適合性を満たすために新しいメンバーを提供することはできません。これは、あるモジュールの基本クラスにプロトコルを追加し、別のモジュールにサブクラスを作成できるため重要です。

したがって、この動作の場合、すでにコンパイルされたサブクラスには、別のモジュールで事後に追加されたスーパークラスの適合からの PWT が欠落し、問題が発生します。


BaseClass他の人が既に述べているように、この場合の 1 つの解決策は、 が の独自の実装を提供することです。このメソッドは、拡張メソッドではなく、 の PWTmethodA()内にあります。BaseClass

もちろん、私たちが扱っているのはクラスBaseClassここでは、単にリストされているメソッドの実装ではなく、ドンその後、クラスの vtable (クラスがポリモーフィズムを実現するメカニズム) を通じて動的にディスパッチされます。したがってSubClass、たとえば、 のオーバーライドを呼び出すことになりますmethodA()

おすすめ記事