当前位置: 首页 > 面试题库 >

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

饶滨海
2023-03-14
问题内容

当我尝试更改实例的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



 类似资料:
  • 问题内容: 我对Python 3中的和类有些困惑。也许有人可以消除我的困惑或提供一些其他信息。 我目前的理解是,每个类(除外)都从称为的基类继承。但是每个类(包括)也是该类的一个实例,它是自身的实例,并且也从继承。 我的问题是: 是否有一个原因/设计决策,为什么是的实例并从中继承?对象的/ class是否也可以是对象本身? 类()如何成为其自身的实例? 哪一个是真正的基类或? 我一直认为这将是最“

  • 在使用来自Java背景的Swift时,为什么要选择结构而不是类呢?似乎它们是一样的,结构提供的功能较少。那为什么选择它呢?

  • Monoid函数的类型签名是:

  • 我读到了这个关于不可变对象的问题,留下了一个关于不可变对象和final字段的问题: 为什么我们需要不可变类中的实例变量成为最终变量? 例如,考虑这个不可变类: 如果在上面的代码中没有设置方法,并且实例变量只在构造函数中设置,那么为什么需要将实例变量声明为final?

  • 我有以下功能: <代码>坐标 这是它的重载==运算符: 如果(c==i),它会在以下位置给我一个错误: 错误:传递“常量坐标”

  • 问题内容: 我是一个相对较新的QA工程师,致力于学习Selenium(使用Java),并且我想使用页面对象为页面建模。 目前,我的页面对象类是静态变量(用于定位页面元素的对象)和静态方法(用于获取By对象并执行页面功能)的集合。这对我来说似乎是最简单的方法,因为我的方法不需要依赖任何实例变量,而只需依赖定位符。 我只是在测试代码中根据需要调用这些方法。 但是,我读到的有关页面对象的所有内容都涉及实