考虑这样的代码:
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开始表现得像一个值类型 - 模仿它的内部状态导致myProperty的“didSet”回调被触发。正如SomeProtocolImplementation将是struct ...
我实际上找到了解决方案,但我也想了解发生了什么。解决方案是将SomeProtocol定义修改为:
protocol SomeProtocol: class {
var something: Bool { get set }
}
它工作正常,但我试图理解为什么它会像这样。有人能解释一下吗?
答案 0 :(得分:0)
任何可以提供对其他类有用的行为的类都可以声明一个编程接口,用于匿名出售该行为。任何其他类可以选择采用协议并实现其一个或多个方法,从而利用该行为。声明协议的类如果由协议采用者实现,则应该调用协议中的方法。
当您尝试设置'值为只读变量 - 您正在尝试更改协议的实现。符合类只能使用协议中的信息。在Swift中,我们可以编写协议扩展,我们可以为协议提供替代方法。
简而言之,将计算变量视为函数。在这种情况下,您在技术上尝试更改功能。
答案 1 :(得分:0)
首先阅读Class Only Protocol的内容。专注于说明的注释部分:
当该协议的要求定义的行为假定或要求符合类型具有引用语义而不是值语义时,使用仅类协议。
以上引用应该让你了解。
您正在尝试为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
答案 2 :(得分:0)
我实际上找到了解决方案,但我也想了解发生了什么。
我正打算告诉你让SomeProtocol成为一个类协议,但你已经明白了。 - 所以我对你
您了解引用类型和值类型,并了解类协议和非类协议。
好吧,只要SomeProtocol可能被struct(它是非类协议)采用,那么如果你输入作为一个SomeProtocol,那么就是值类型。运行时不会因为采用者变成类实例而打开引用类型行为;所有决定必须在编译时进行。在编译时,所有编译器都知道这个东西是SomeProtocol,其采用者可能是一个结构。