为什么常量会约束结构实例而不是类实例的属性?

斯洛文尼亚语

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

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中的结构是值类型–从语义上讲,值(即值类型的“实例”)是不可变的。

值类型的突变,无论是直接更改属性的值,还是通过使用mutating方法,都相当于为保存该变量的变量分配了一个全新的值(加上该突变触发的任何副作用)。因此,保存该变量的变量必须为var正如iGodric指出的那样,属性值周围的属性观察者的行为很好地展示了这种语义

因此,这意味着您可以想到以下几点:

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)

如您所见,此代码是非法的。您不能将其赋值fooCopyfoo-这是一个let常数。因此,您不能更改声明为a的值类型的属性,let因此需要将其设置为a var

(值得注意的是,编译器实际上并没有经过该操作程序;它可以直接更改结构的属性,这可以通过查看生成的SIL来看到。但这不会改变值类型的语义。)


您可以更改let常量实例的可变属性的原因是由于类是引用类型。因此,let常数只能确保参考值保持不变。更改它们的属性不会以任何方式影响您对它们的引用-您仍在引用内存中相同位置。

您可以将引用类型视为路标,因此代码如下:

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

为什么可以在属性值中更改类的常量实例?

为什么自定义Python类实例的`__dict__`属性是该类的描述符,而不是实例的实际属性?

为什么常量的属性或值不是常量

为什么这会编译对类而不是实例的引用?

为什么toBinaryString不是Integer类中的实例方法?

为什么在原型而不是构造函数中声明实例属性?

为什么不能在类的实例上设置属性?

为什么C#编译器可以“看到”未引用的DLL中的类的静态属性,而不是其实例方法?

为什么我可以更改/重新分配从类实例化的常量值

C#-为什么在引用类常量时执行会流到实例变量?

实例声明中的类型约束,但不是类

为什么iter不是实例的方法而__iter__是实例?

为什么使用类方法而不是仅使用实例的类名?

为什么Kleisli不是Monoid的实例?

为什么4不是Number的实例?

为什么Exception不是Throwable实例?

为什么 Laravel 实例化一个类来获取一个属性而不是将该属性设置为静态?

为什么在对象实例化后定义的属性不是自己的属性

为什么在此Functor的类实例中不能接受这种保留结构的“ fmap”?

结构实例属性值

为什么可以调用类的实例?

为什么类返回自己的实例?

静态类中的常量实例

为什么实例的属性未定义?

为什么实例模型的属性不变

为什么从调用容器类,而不是在Java中它的实例方法

为什么在使用反射调用方法时出现“对象不是声明类的实例”的问题?

为什么在类中定义的变量不是局部变量而是Java中的实例变量?

为什么方法局部静态变量绑定到类而不是实例?