2017-08-23 44 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,其采用者可能是一个结构体。