2017-08-23 121 views
2

的內部屬性時,請考慮這樣的代碼只能在協議中定義的屬性會導致編譯錯誤:獲取修改對象

protocol SomeProtocol { 
    var something: Bool { get set } 
} 

class SomeProtocolImplementation: SomeProtocol { 
    var something: Bool = false { 
     didSet { 
      print("something changed!") 
     } 
    } 
} 

protocol MyProtocol { 
    var myProperty: SomeProtocol { get } 
} 

class MyClass: MyProtocol { 
    var myProperty: SomeProtocol = SomeProtocolImplementation() { 
     didSet { 
      print("myProperty has changed") 
     } 
    } 
} 


var o: MyProtocol = MyClass() 
o.myProperty.something = true 

此代碼不能用錯誤編譯:

error: cannot assign to property: 'myProperty' is a get-only property 
o.myProperty.something = true 
~~~~~~~~~~~~   ^

爲什麼?我的屬性是SomeProtocolImplementation類型,它是類類型,所以應該可以使用對myProperty的引用來修改它的內部屬性。

的進一步深入,修改myProperty的定義,使它看起來像後:

var myProperty: SomeProtocol { get set } 

奇怪的事情發生了。現在代碼編譯(並不意外),但輸出是:

something changed! 
myProperty has changed 

所以在這一點SomeProtocolImplementation開始表現得像一個值類型 - modyifing它的內部狀態導致了「didSet」回調myProperty的被觸發。正如SomeProtocolImplementation將結構...

我實際上找到解決方案,但我也想知道發生了什麼事。解決方案是修改SomeProtocol定義爲:

protocol SomeProtocol: class { 
    var something: Bool { get set } 
} 

它工作正常,但我想明白爲什麼它的行爲是這樣的。任何人都可以解釋?

回答

0

Any class that can provide behavior useful to other classes may declare a programmatic interface for vending that behavior anonymously. Any other class may choose to adopt the protocol and implement one or more of its methods, thereby making use of the behavior. The class that declares a protocol is expected to call the methods in the protocol if they are implemented by the protocol adopter.

Protocol Apple Documentation

當您嘗試「設置」值是一個變量只讀 - 你正在試圖更改協議的執行情況。符合類只能使用來自協議的信息。在Swift中,我們可以編寫協議擴展,我們可以爲協議提供其他方法。

In short think of computed variables as functions. You are technically trying to change a function in this case.

0

首先閱讀Class Only Protocol是什麼。專注於說明部分:

Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics.

以上引用應該讓你的想法。

您正試圖獲得引用類型的行爲爲您SomeProtocol的共形類(即SomeProtocolImplementation)。您希望將來能夠更改something的值。所以基本上你是指向以上報價的句子。

如果你需要更多的說明,請考慮以下更有意義的設計,我改變了命名爲方便:

protocol Base: class { 
    var referenceTypeProperty: Bool { get set } 
    // By now you are assuming: this property should be modifiable from any reference. 
    // So, instantly make the protocol `Class-only` 
} 

class BaseImplementation: Base { 
    var referenceTypeProperty: Bool = false { 
     didSet { 
      print("referenceTypeProperty did set") 
     } 
    } 
} 

protocol Child { 
    var valueTypeProperty: Base { get } 
    // This property shouldn't be modifiable from anywhere. 
    // So, you don't need to declare the protocol as Class-only 
} 

class ChildImplementation: Child { 
    var valueTypeProperty: Base = BaseImplementation() { 
     didSet { 
      print("valueTypeProperty did set") 
     } 
    } 
} 

let object: Child = ChildImplementation() 
object.valueTypeProperty.referenceTypeProperty = true 
0

I actually find the solution, but I want also understand what's going on.

我正要告訴你,使SomeProtocol一類協議,但你已經知道了。 - 所以我有點困惑,你不瞭解

您瞭解引用類型和值類型,並且瞭解類協議和非類協議。

嘛,只要SomeProtocol可能是由一個結構(這是一個非階級協議)被採納,那麼如果你輸入的東西一個SomeProtocol,它值類型。運行時不會僅僅因爲採用者變成一個類實例而切換引用類型行爲;所有的決定都必須在編譯時進行。在編譯時,所有編譯器都知道這是SomeProtocol,其採用者可能是一個結構體。