在swift中,为什么我可以通过可选链接设置多态变量的计算属性,而不能在未包装的可选链接上设置?
我在五月的应用程序中遇到了一个奇怪的错误。在这个问题的底部是完整的代码,它再现了我在应用程序中看到的内容,但这里有一个快速的演示在swift中,为什么我可以通过可选链接设置多态变量的计算属性,而不能在未包装的可选链接上设置?,swift,Swift,我在五月的应用程序中遇到了一个奇怪的错误。在这个问题的底部是完整的代码,它再现了我在应用程序中看到的内容,但这里有一个快速的演示 我创建了同一类的两个实例,一个声明为符合协议的可选实例,另一个声明为具体类的可选实例 对于这两种情况,我可以通过选项链接设置计算属性,即: anOptionalInstance?.someComputedProperty = .... 对于具体版本,我可以通过展开可选的 if let anInstance = anOptionalInstance { anIn
我创建了同一类的两个实例,一个声明为符合协议的可选实例,另一个声明为具体类的可选实例 对于这两种情况,我可以通过选项链接设置计算属性,即:
anOptionalInstance?.someComputedProperty = ....
对于具体版本,我可以通过展开可选的
if let anInstance = anOptionalInstance {
anInstance.someComputedProperty = ....
}
对于多态版本,我收到一条错误消息,说我无法在实例上设置属性
下面是一个完整的文件,复制了我看到的问题 有人能解释一下这里发生了什么吗
struct MyStruct {
var someMember: String
}
protocol MyProtocol {
var myVar: MyStruct { get set }
}
class MyType: MyProtocol {
var myVar: MyStruct {
get {
return MyStruct(someMember: "some string")
}
set {
println(newValue)
}
}
}
class UsingClass {
var anInstanceOfMyType: MyProtocol?
var anOtherInstanceOfMyType: MyType?
func someMethod() {
anInstanceOfMyType = MyType()
anInstanceOfMyType?.myVar = MyStruct(someMember: "blah")
if let anInstanceOfMyType = anInstanceOfMyType {
// The following line produces this error :Cannot assign to 'myVar' in 'anInstanceOfMyType'
anInstanceOfMyType.myVar = MyStruct(someMember: "blah blah")
}
anOtherInstanceOfMyType = MyType()
anOtherInstanceOfMyType?.myVar = MyStruct(someMember: "blah")
if let anOtherInstanceOfMyType = anOtherInstanceOfMyType {
anOtherInstanceOfMyType.myVar = MyStruct(someMember: "blah blah")
}
}
}
出现此问题的原因是您试图更改常量的属性
anInstanceOfMyType
哪种类型是MyProtocol
1.为什么anInstanceOfMyType
是常数?
在使用class的的第一行,instanceofmytype
实际上被声明为var
。但是,使用条件展开
会创建一个名为anInstanceOfMyType
的常量,并且您正在尝试更改该常量的属性
2.好的,但是anInstanceOfMyType
引用了一个类的实例,所以我应该能够更改它的属性,即使它是一个常量
由于anInstanceOfMyType
将MyProtocol
作为类型,因此它可以包含一个struct
或一个类的实例引用。
因此,编译器确实应用了更安全的方法,避免您更改其属性
解决方案
通过将class关键字添加到协议的继承列表中,将协议采用限制为类类型(而不是结构或枚举)。class关键字必须始终出现在协议继承列表的第一位,在任何继承协议之前:
protocol MyProtocol: class {
var myVar: MyStruct { get set }
}
或
如果MyProtocol
更新为扩展AnyObject
protocol MyProtocol : AnyObject {
var myVar: MyStruct { get set }
}
然后就清楚了,anInstanceOfMyType
必须引用一个类的实例,在这种情况下,您的代码是可以编译的。我想我仍然很困惑,anInstanceOfMyType是一个类的实例(引用类型),而不是一个结构(值类型),所以我认为应该让它工作,因为我没有更改引用,让我们为具体案例工作。但是,我正在设置的属性myVar是一个结构。另一种表达我困惑的方式是:那么,为什么我不需要var来处理具体的案例呢?很好。我需要看一下官方文件来回答这一评论。我认为这个想法是anInstanceOfMyType
的类型是MyProtocol
。因此,对于编译器,这个变量可以包含类或结构的实例。然后,编译器允许您更改MyProtocol
类型变量的属性。但是它不允许你改变MyProtocol
类型的常数的属性。。。我要说的是,协议只能是classesCorrect。事实上,如果MyProtocol
扩展了AnyObject
,您的原始代码就可以编译了。实际上,只需将其定义为protocol MyProtocol:class{