Swift 中的结构是 value types - 从语义上讲,值(即值类型的“实例”)是不可变的。
值类型的突变,无论是通过直接更改属性的值,还是通过使用mutating 方法,都等同于将一个全新的值赋给该变量持有它(加上突变触发的任何副作用)。因此,持有它的变量必须是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 保持不变。改变它们的属性不会以任何方式影响您对它们的引用——您仍然指的是内存中的 same 位置。
您可以将引用类型想象为路标,因此代码如下:
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 |
只有当您尝试将 new 引用分配给 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 才能使此分配合法。