swift - 为什么常量约束来自结构实例而不是类实例的属性?

标签 swift class structure constants

当我尝试更改 byValueObj 实例的 ID 属性时,我收到一条错误消息,告诉我无法分配给常量的属性,即使属性是一个变量。但是,我可以在类实例上执行此操作。我有点知道它可能与按值和按引用机制有关。但是我对它并没有很清楚和正确的认识。有人可以为我解释一下吗?谢谢。

struct CreatorValue{
    var ID = 2201
}
class CreatorRefer{
    var ID = 2203
}

let byValueObj = CreatorValue()
let byReferObj = CreatorRefer()

byValueObj.ID = 201 //Error: cannot assign to property: 'byValueObj' is a 'let' constant
byReferObj.ID = 203 //works fine here

最佳答案

Swift 中的结构是 value types – 而且,从语义上讲,值(即值类型的“实例”)是不可变的。

值类型的变异,无论是通过直接改变属性的值,还是通过使用变异方法,都等同于分配一个全新的保存它的变量的值(加上突变触发的任何副作用)。因此,保存它的变量需要是 var。围绕值类型的属性观察者的行为很好地展示了这种语义,as iGodric points out .

那么这意味着你可以这样想:

struct Foo {
    var bar = 23
    var baz = 59
}

// ...

let foo = Foo()
foo.bar = 7 // illegal

这样做:

let foo = Foo()

var fooCopy = foo // temporary mutable copy of foo.

fooCopy.bar = 7   // mutate one or more of the of the properties

foo = fooCopy     // re-assign back to the original (illegal as foo is declared as
                  // a let constant)

如您所见,此代码是非法的。您不能将 fooCopy 赋值回 foo – 因为它是一个 let 常量。因此,您无法更改声明为 let 的值类型的属性,因此需要将其设为 var

(值得注意的是,编译器实际上并没有通过这个 palaver;它可以直接改变结构的属性,这可以通过查看 the SIL generated 看出。这不会改变的语义值类型。)


之所以可以更改 let 常量 class 实例的可变属性,是因为类是引用类型。因此,作为一个 let 常量只能确保 reference 保持不变。改变它们的属性不会以任何方式影响您对它们的引用——您仍然引用内存中的相同位置。

您可以将引用类型想象成路标,因此代码如下:

class Foo {
    var bar = 23
    var baz = 59
}

// ...

let referenceToFoo = Foo()

你可以这样想内存表示:

|    referenceToFoo     |  --->  | Underlying Foo instance |
| (a reference to 0x2A) |        |<----------------------->|
                                 |0x2A       |0x32         |0x3A
                                 |  bar: Int |  baz : Int  |
                                 |     23    |      59     |

当你改变一个属性时:

referenceToFoo.bar = 203

引用 (referenceToFoo) 本身 不受影响——您仍然指向内存中的相同位置。改变的是底层实例的属性(意味着底层实例发生了变异):

|    referenceToFoo     |  --->  | Underlying Foo instance |
| (a reference to 0x2A) |        |<----------------------->|
                                 |0x2A       |0x32         |0x3A
                                 |  bar: Int |  baz : Int  |
                                 |    203    |      59     |

只有当您尝试为 referenceToFoo 分配一个引用时,编译器才会给您一个错误,因为您正试图改变引用本身:

// attempt to assign a new reference to a new Foo instance to referenceToFoo.
// will produce a compiler error, as referenceToFoo is declared as a let constant.
referenceToFoo = Foo()

因此,您需要将 referenceToFoo 设为 var 以使此分配合法。

关于swift - 为什么常量约束来自结构实例而不是类实例的属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38010936/

相关文章:

ios - 当我在 Parse Swift 2.2 中遇到未经授权的错误时该怎么办

javascript - 纯函数可以使用 `this` 改变其容器类内的其他属性吗?

c++ - 初始化 A(ob) 是如何工作的,其中 ob 是 A 类的对象

c++ - 在 C++ 中调用作为结构数组成员的成员函数指针的语法是什么

c - 将字符串分配给 C 结构中的元素

swift - FIRAuth.auth() 编译错误 : "Cannot use optional chaining on non optional value ' FIRAuth'"

ios - 自定义 UITableViewHeaderFooterView 标签被截断

c++ - "Error: no matching function for call distance::distance()"...无法从 main 中的类声明对象?

c++ - 初始化结构数组 C++ 时出现问题

ios 10 Swift 电话未在某些号码上发起