为什么常量会约束结构实例而不是类实例的属性? [英] Why constant constraints the property from a structure instance but not the class instance?

查看:64
本文介绍了为什么常量会约束结构实例而不是类实例的属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我尝试更改 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 常量 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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆