2016-11-05 10 views
4

私は次のようにしなければならないと思っているものがなぜ印刷されないのだろうと思っています。プロトコルの拡張子とサブクラス

/* Fails */ 
protocol TheProtocol { 
    func update() 
} 

class A: TheProtocol { 
} 

class B : A {} 

extension TheProtocol { 
    func update() { 
     print("Called update from TheProtocol") 
    } 
} 

extension TheProtocol where Self: B { 
    func update() { 
     print("Called update from B") 
    } 
} 

let instanceB = B() 
instanceB.update() 

let instanceBViaProtocol:TheProtocol = B() 
instanceBViaProtocol.update() 

これは、印刷され、次の

Called update from B 
Called update from TheProtocol // Why not: Called update from B (extension) 

instanceBViaProtocol.update() 

がTheProtocolに拡張して()更新を実行しませんなぜ私は特に疑問に思って:

extension TheProtocol where Self: B { 
    func update() { 
     print("Called update from B") 
    } 
} 

私はそれがBはTheProtocolを採用しているAから継承しているので、Bは暗黙のうちにTheProtocolも採用すると思います。 プロトコルの採用をAからBに移動すると、期待される結果が得られます。

protocol TheProtocol { 
    func update() 
} 

class A { // Remove TheProtocol 
} 

class B : A, TheProtocol {} // Add TheProtocol 

extension TheProtocol { 
    func update() { 
     print("Called update from TheProtocol") 
    } 
} 

extension TheProtocol where Self: B { 
    func update() { 
     print("Called update from B") 
    } 
} 

let instanceB = B() 
instanceB.update() 

let instanceBViaProtocol:TheProtocol = B() 
instanceBViaProtocol.update() 

結果:

Called update from B 
Called update from B 

私はhttps://medium.com/ios-os-x-development/swift-protocol-extension-method-dispatch-6a6bf270ba94#.6cm4oqaq1http://krakendev.io/blog/subclassing-can-suck-and-heres-whyで見ていたが、私はこれを理解することができませんでした。拡張メソッドは、プロトコルを採用するエンティティのサブクラスでは尊重されないのですか?

+0

'拡張機能TheProtocolをSelf:B {'〜 '拡張機能TheProtocol where Self:A {'とあなたに何か説明しているかを見てください。 –

+0

チップをありがとう。 – user6902806

答えて

0

答えは方法発送 + a Bug in Swiftです。

メソッドディスパッチは、メソッドが呼び出されたときに実行する実装をコンパイラが選択するために使用するメカニズムです。 Swiftは3種類のメソッドディスパッチを使用します。あなたはそれについて読むことができますhere

ディスパッチ方法は、インスタンスの種類ではなく、参照の種類によって決まります。あなたの場合、リファレンスタイプはTheProtocolです。

let instanceBViaProtocol:TheProtocol = B() 
instanceBViaProtocol.update() 

要件メソッドの共通の動作を定義するプロトコル拡張があります。この場合、動的ディスパッチが使用されます。つまり、Bで宣言された実装を使用する必要があります。しかし、問題の原因となるのはa bug in Swiftです。

それぞれのタイプについて、Swiftは、ダイナミックディスパッチに使用される実装を登録するためにウォッチテーブルを使用します。このバグにより、BクラスはTheProtocolのWitnessテーブルにupdate()の実装を登録できません。また、TheProtocolテーブルを使用して更新がディスパッチされると、間違った実装が使用されます。

ここにいくつかの変更点があります。スーパークラスでupdateを宣言し、それをサブクラスでオーバーライドすると、期待どおりに動作することに注意してください。バグを見て分かりやすい方法です。

protocol TheProtocol { 
    func update() 
} 

class A: TheProtocol { 
    func update(){ 
     print("Implementation of A") 
    } 
} 

class B : A { 
    override func update(){ 
     print("Implementation of B") 
    } 
} 

//All those who conform to TheProtocol will execute this. 
extension TheProtocol { 
    func update() { 
     print("Common: TheProtocol") 
    } 
} 
extension TheProtocol where Self: B { 
    func update() { 
     print("Common: TheProtocol for B's") 
    } 
} 
extension TheProtocol where Self: A { 
    func update() { 
     print("Common: TheProtocol for A's") 
    } 
} 


let instanceBViaProtocol:TheProtocol = B() //It prints "Implementation of B" 
instanceBViaProtocol.update() 

私はこれがあなたの質問に答えることを願っています。

https://www.raizlabs.com/dev/2016/12/swift-method-dispatch/は、迅速なメソッドディスパッチについてすばらしい説明があります。

Here私はプロトコル拡張でメソッドディスパッチについて書いた短いことを読むことができます。