Ios 协议中定义的仅获取属性在修改对象的内部属性时导致编译错误

Ios 协议中定义的仅获取属性在修改对象的内部属性时导致编译错误,ios,swift,Ios,Swift,考虑这样的代码: protocol SomeProtocol { var something: Bool { get set } } class SomeProtocolImplementation: SomeProtocol { var something: Bool = false { didSet { print("something changed!") } } } protocol MyProtocol

考虑这样的代码:

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
~~~~~~~~~~~~           ^
为什么??My属性属于SomeProtocolImplementation类型,它是类类型,所以应该可以使用对myProperty的引用来修改它的内部属性

更进一步,在修改myProperty定义使其看起来如下:

var myProperty: SomeProtocol { get set }
奇怪的事情发生了。现在可以编译代码(这并不奇怪),但输出是:

something changed!
myProperty has changed
因此,在这一点上,一些ProtocolImplementation开始表现得像一个值类型——modyifing它的内部状态会导致myProperty的“didSet”回调被触发。就像一些协议实现是结构化的一样

我确实找到了解决办法,但我也想了解到底发生了什么。解决方案是将协议定义修改为:

protocol SomeProtocol: class {
    var something: Bool { get set }
}
它工作得很好,但我试图理解它为什么会这样。有人能解释吗

任何可以提供对其他类有用的行为的类都可以声明一个编程接口,用于匿名出售该行为。任何其他类都可以选择采用该协议并实现其一个或多个方法,从而利用该行为。如果协议采用者实现了协议中的方法,则声明协议的类将调用这些方法

当您尝试将值“设置”为只读变量时,您正在尝试更改协议的实现。一致性类只能使用来自协议的信息。在Swift中,我们可以编写协议扩展,在那里我们可以为协议使用其他方法

简言之,将计算变量视为函数。在这种情况下,您在技术上试图更改函数

首先读什么是。集中注意注释部分,该部分说明:

当协议要求定义的行为假设或要求一致性类型具有引用语义而非值语义时,使用仅类协议

上面的引语应该能让你明白这一点

您正试图为您的
SomeProtocol
的一致性类(即
SomeProtocolImplementation
)获取引用类型的行为。您希望将来能够更改
某物的值。所以基本上你是指向上面引用的句子

如果您需要更多的澄清,请考虑下面的更有意义的设计,我为方便起见更改了命名:

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
我确实找到了解决办法,但我也想了解到底发生了什么

我正要告诉你把某个协议变成一个类协议,但你已经明白了。-所以我对你不明白的地方有点困惑

您了解引用类型和值类型,并且了解类协议和非类协议

好的,只要一个结构(它是一个非类协议)可以采用SomeProtocol,那么如果您将某个东西作为SomeProtocol输入,它就是一个值类型。运行时不会仅仅因为采纳者是类实例而启用引用类型行为;所有的决定都必须在编译时做出。在编译时,编译器只知道这是一个SomeProtocol,它的使用者可能是一个结构