2016-11-05 167 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 where Self:B {'to'extension 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表發送時,會使用錯誤的實現。

在這裏你有你的例子有一些變化。請注意,如果您在超類中聲明瞭更新並在子類中覆蓋它,它將按預期工作。這是看到bug的最明顯的方式。

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/對swift方法調度有一個很好的解釋。

Here你可以閱讀我寫的關於協議擴展中的方法調度的簡短的東西。