隐式解包可选项不可变

10

为什么我无法修改一个隐式解包可选变量?

以下是一个简短的示例,可重现此问题:

使用数组

var list: [Int]! = [1]
list.append(10) // Error here

类型为 '[Int]' 的不可变值仅具有名为 'append' 的可变成员

使用 Int

var number: Int! = 1
number = 2
number = 2 + number
number += 2 // Error here

找不到接受所提供参数的“+=”的重载

2个回答

5
因为你尝试突变的方式是通过突变值(即不可突变值)而不是突变变量,这在Swift中的值类型都是不可变的。
突变并不是对值的突变,而是对包含该值的变量进行突变。
在Int的情况下,+=运算符会在左边得到一个结构体,在右边得到一个整数,它无法将结构体添加到整数中。
在数组的情况下,append是一个突变成员。但它被调用时作用于一个不可变值,该值未直接存储在变量中。它只能操作直接存储在变量中的值(这就使它们成为可变的:它们存储在变量中。它们实际上并不是可变的,变量才是)。

错误。在Swift中,像String这样的值类型和像Int[]这样的集合类型默认情况下是可变的,当赋值给变量时。只有当你将它们赋值给常量(即let)时,它们才变成不可变的。请参考 文档 - akashivskyy
4
可变的是变量,而不是值。对于值类型来说,值始终是不可变的。文档有时以非正式的方式表述,有时更为正式。当它说将字符串或数组分配给“var”时是可变的,分配给“let”时是不可变的时,它只是在说该值本身是不可变的,但您可以将其分配给可变的“var”,并且可以改变该变量的值。这就是为什么您不能调用任何作为另一种类型构造函数参数的对象的可变方法的原因。 - Analog File
如果您创建一个数组或字典并将其分配给一个变量,那么所创建的集合将是可变的。我不明白这有什么不清楚的。 - akashivskyy
3
这句话并不含糊,只是它的意思不是你所想的那样。它的意思是你可以调用该值的可变方法。而可变方法会接收一个隐藏参数,即变量的地址,创建一个新值,并将其赋给该变量。它们不会改变实际的原始值(除非编译器能够证明不存在别名或共享,这时它可以执行优化并实际改变原始存储,但这是透明的)。 - Analog File
1
特别注意,对于数组来说,修改数组意味着修改长度而不是修改内容。实际上,这就是为什么 OP 中的append调用失败的原因:它是一个数组的变异方法。它需要创建一个新数组并将其分配给一个变量,但只有在有一个包含数组的变量时才能这样做,而没有包含数组的变量。 - Analog File
显示剩余7条评论

4

更新:

这个问题在Xcode Beta 5中已经修复,但有一个小问题:

var list: [Int]! = [1]
list.append(10)

var number: Int! = 1
number! += 2
number += 2 // compile error

数组按预期工作,但现在整数仍需要显式解包才能使用 +=


目前,这只是可选项的本质(无论是否隐式解包)。解包操作符返回一个不可变值。未来可能会修复此问题或提供更好的解决方案

现在唯一的解决办法是将数组封装在类中:

class IntArray {
    var elements : [Int]
}

解包运算符返回一个不可变的值,因为这是不可变操作的本质。正如Lattner所说,没有明显的解决方案。将解包操作变为可变的并不是一个解决方案:您将无法在不可变的Optional上使用它,而且它也无法解决问题,因为返回的值仍然是不可变的(因为所有值类型始终是不可变的)。 - Analog File

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接