为什么常量会约束结构实例而不是类实例的属性? [英] Why constant constraints the property from a structure instance but not the class instance?
问题描述
当我尝试更改 byValueObj
实例的 ID
属性时,我收到一条错误消息,告诉我即使该属性是变量,也无法将其分配给常量的属性。但是,我可以在类实例上执行此操作。我有点知道它可能与按值和按引用机制有关。但是我对此并不十分清楚正确。有人可以帮我解释一下吗?谢谢。
struct CreatorValue {
var ID = 2201
}
class CreatorRefer {
var ID = 2203
}
let byValueObj = CreatorValue()
let byReferObj = CreatorRefer()
byValueObj.ID = 201 / /错误:无法分配给属性:'byValueObj'是'let'常量
byReferObj.ID = 203 //在这里正常工作
Swift中的结构为值类型 –从语义上讲,值(即值类型的实例)是不可变的。
值类型的突变(通过直接更改属性的值,或使用 muting
方法),等效于将全新值分配给保存它的变量(加上触发突变的任何副作用)。因此,保存该变量的变量必须为 var
。正如iGodric指出的那样, iGodric指出的。而且,属性观察者围绕值类型的行为很好地展示了这种语义。 b
$ b
所以这意味着您可以想到:
struct Foo {
var bar = 23
var baz = 59
}
// ...
let foo = Foo()
foo.bar = 7 //非法
这样做:
let foo = Foo()
var fooCopy = foo // foo的临时可变副本。
fooCopy.bar = 7 //更改其中一个或多个属性
foo = fooCopy //重新分配回原始属性(声明foo时非法如
//一个let常量)
您可以清楚地看到–该代码是非法。您不能将 fooCopy
分配回 foo
–因为它是 let
常数。因此,您不能更改声明为 let
的值类型的属性,因此需要将其设置为 var
。
(值得注意的是,编译器实际上并没有经过这个palaver;它可以直接改变结构的属性,这可以看出通过查看生成的SIL 。尽管如此,这不会改变值类型的语义。)
可以更改 let $ c的可变属性的原因$ c>常量 class 实例,是由于类是引用类型。因此,作为
let
常量只能确保 reference 保持不变。更改它们的属性不会以任何方式影响您对它们的引用-您仍在引用内存中的相同位置。
您可以想到一个类似路标的引用类型,因此代码如下:
class Foo {
var bar = 23
var baz = 59
}
// ...
让referenceToFoo = Foo()
您可以想到这样的内存表示形式:
| referenceToFoo | ---> |基础Foo实例|
| (对0x2A的引用)| |< -----------------------> |
| 0x2A | 0x32 | 0x3A
|条:整数| baz:Int |
| 23 | 59 |
当您对某项财产进行突变时:
referenceToFoo.bar = 203
参考( referenceToFoo
)本身不受影响–您仍指向内存中的相同位置。更改的是基础实例的属性(意味着基础实例已更改):
| referenceToFoo | ---> |基础Foo实例|
| (对0x2A的引用)| |< -----------------------> |
| 0x2A | 0x32 | 0x3A
|条:整数| baz:Int |
| 203 | 59 |
仅当您尝试为<$ c $分配 new 引用时c> referenceToFoo 会导致编译器在尝试更改引用本身时给您一个错误:
//尝试将对新Foo实例的新引用分配给referenceToFoo。
//将产生编译器错误,因为referenceToFoo被声明为let常量。
referenceToFoo = Foo()
因此,您需要使 referenceToFoo
一个 var
,以使此分配合法。
When I trying to change the ID
property of the byValueObj
instance, I received an error that told me I cannot assign to the property of a constant, even though the property is a variable. However, I can do it on a class instance. I kind of knowing that it maybe has something to do with the by value and by reference mechanism. But I don't have a very clear and correct understanding of it. Can someone explain it for me? Thanks.
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
Structures in Swift are value types – and, semantically speaking, values (i.e 'instances' of value types) are immutable.
A mutation of a value type, be it through directly changing the value of a property, or through using a mutating
method, is equivalent to just assigning a completely new value to the variable that holds it (plus any side effects the mutation triggered). Therefore the variable holding it needs to be a var
. And this semantic is nicely showcased by the behaviour of property observers around value types, as iGodric points out.
So what this means is that you can think of this:
struct Foo {
var bar = 23
var baz = 59
}
// ...
let foo = Foo()
foo.bar = 7 // illegal
as doing this:
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)
And as you can clearly see – this code is illegal. You cannot assign fooCopy
back to foo
– as it's a let
constant. Hence, you cannot change the property of a value type that is declared as a let
, and would therefore need make it a var
.
(It's worth noting that the compiler doesn't actually go through this palaver; it can mutate the properties of structures directly, which can be seen by looking at the SIL generated. This doesn't change the semantics of value types though.)
The reason you can change a mutable property of a let
constant class instance, is due to the fact that classes are reference types. Therefore being a let
constant only ensures that the reference stays the same. Mutating their properties doesn't in any way affect your reference to them – you're still referring to the same location in memory.
You can think of a reference type like a signpost, therefore code like this:
class Foo {
var bar = 23
var baz = 59
}
// ...
let referenceToFoo = Foo()
you can think of the memory representation like this:
| referenceToFoo | ---> | Underlying Foo instance |
| (a reference to 0x2A) | |<----------------------->|
|0x2A |0x32 |0x3A
| bar: Int | baz : Int |
| 23 | 59 |
And when you mutate a property:
referenceToFoo.bar = 203
The reference (referenceToFoo
) itself isn't affected – you're still pointing to the same location in memory. It's the property of the underlying instance that's changed (meaning the underlying instance was mutated):
| referenceToFoo | ---> | Underlying Foo instance |
| (a reference to 0x2A) | |<----------------------->|
|0x2A |0x32 |0x3A
| bar: Int | baz : Int |
| 203 | 59 |
Only when you attempt to assign a new reference to referenceToFoo
will the compiler give you an error, as you're attempting to mutate the reference itself:
// 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()
You would therefore need to make referenceToFoo
a var
in order to make this assignment legal.
这篇关于为什么常量会约束结构实例而不是类实例的属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!