Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在swift中,为什么我可以通过可选链接设置多态变量的计算属性,而不能在未包装的可选链接上设置?_Swift - Fatal编程技术网

在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{