2017-03-02 32 views
12

爲什麼下面的代碼會產生錯誤?爲什麼協議中的只能獲取屬性要求不能通過符合的屬性滿足?

protocol ProtocolA { 
    var someProperty: ProtocolB { get } 
} 

protocol ProtocolB {} 
class ConformsToB: ProtocolB {} 

class SomeClass: ProtocolA { // Type 'SomeClass' does not conform to protocol 'ProtocolA' 
    var someProperty: ConformsToB 

    init(someProperty: ConformsToB) { 
     self.someProperty = someProperty 
    } 
} 

The answer in this similar question是有意義的。但是,在我的示例中,該屬性是隻能獲取的。爲什麼不應該這樣工作?這是Swift的一個缺點嗎,還是有一些合理的理由?

+0

感謝您的聯繫。這是不幸的,但很高興知道! – solidcell

+1

如果你想要這種行爲,在'ProtocolA'中你應該有'associatedtype T:ProtocolB',然後聲明'var someProperty:T {get}' – BallpointBen

+0

這將作爲解決方法在此期間,直到它(希望)是固定的,但我真的很猶豫要添加一個關聯類型,因爲這會將這些知識引發到對象圖的其餘部分,而這些對象圖很快就會失去控制。 – solidcell

回答

12

有沒有真正的原因,這不應該是可能的,一個只讀屬性要求可以是協變,從類型爲ProtocolB屬性返回ConformsToB實例是完全合法的。

Swift目前不支持它。爲此,編譯器必須在協議見證表和符合實現之間生成a thunk以執行必要的類型轉換。例如,一個ConformsToB實例將需要盒裝in an existential container爲了被輸入爲ProtocolB(並且調用者無法做到這一點,因爲它可能不知道被調用實現的任何內容)。

但是,再次,沒有理由爲什麼編譯器不應該能夠做到這一點。有多種錯誤報告在這個開放的,this one這是特定於只讀屬性的要求,this general one,其中斯拉瓦佩斯托夫,雨燕小組的成員,他說:

[...]我們要在允許功能轉換的每一種情況下,協議證人和方法覆蓋

因此它絕對看起來像Swift團隊希望在未來版本的語言中實現的東西。

然而在此同時,爲@BallpointBen says,一個解決方法是使用一個associatedtype

protocol ProtocolA { 
    // allow the conforming type to satisfy this with a concrete type 
    // that conforms to ProtocolB. 
    associatedtype SomeProperty : ProtocolB 
    var someProperty: SomeProperty { get } 
} 

protocol ProtocolB {} 
class ConformsToB: ProtocolB {} 

class SomeClass: ProtocolA { 

    // implicitly satisfy the associatedtype with ConformsToB. 
    var someProperty: ConformsToB 

    init(someProperty: ConformsToB) { 
     self.someProperty = someProperty 
    } 
} 

但是,這是相當不令人滿意,因爲它意味着ProtocolA不再可用作類型(因爲它有associatedtype要求)。它也改變協議所說的內容。它最初說,someProperty可以返回任何是符合ProtocolB - 現在它說的someProperty涉及符合ProtocolB只是一個特定具體類型的實現。

另一個解決方法是僅僅爲了滿足協議要求定義的虛擬財產:

protocol ProtocolA { 
    var someProperty: ProtocolB { get } 
} 

protocol ProtocolB {} 
class ConformsToB: ProtocolB {} 

class SomeClass: ProtocolA { 

    // dummy property to satisfy protocol conformance. 
    var someProperty: ProtocolB { 
     return actualSomeProperty 
    } 

    // the *actual* implementation of someProperty. 
    var actualSomeProperty: ConformsToB 

    init(someProperty: ConformsToB) { 
     self.actualSomeProperty = someProperty 
    } 
} 

這裏,我們基本上是寫形實轉換編譯器 - 但它也並不特別漂亮,因爲它向API添加了不必要的屬性。

+1

感謝您的詳細解答@Hamish。我已經做了你所建議的(計算的屬性包裝),但我同意,不得不添加另一個屬性。 – solidcell

相關問題