值类型默认是不可变的
在 Swift 中,结构体(struct)和枚举(enum)是值类型。值类型的实例默认是不可变的,除非:
1、它被声明为 var。
2、属性或方法声明为 mutating 来明确支持修改。
SwiftUI 的限制
在 SwiftUI 中,视图结构的实例(如 ContentView)本质上是不可变的,因为 SwiftUI 使用值类型视图来构建用户界面。即使声明了 var 类型,在 SwiftUI 的 body 或闭包中,这个实例实际上是不可变的,无法直接修改。
问题实例
自定义一个NonNegative的值类型实例,尝试修改实例属性时,触发Swift的不可变性规则,导致编译器报错。
var example = NonNegative(wrappedValue: 5)
example.wrappedValue -= 10 // 报错行
报错内容为:
Left side of mutating operator isn't mutable: 'self' is immutable
报错原因在于,wrappedValue 的 set 中试图修改包装器实例的内部状态。
var wrappedValue: Value {
get { value }
set {
if newValue < 0 {
value = 0
} else {
value = newValue
}
}
}
但这个包装器实例属于不可变的上下文(如 ContentView),因此报错。
解决的思路为,如果修改Swift中值类型的属性,需要确保实例本身是可变的。
Swift 中值类型的可变性规则
1、局部变量的可变性
在函数或方法中,如果值类型实例是用 var 声明的,就可以直接修改:
var example = NonNegative(wrappedValue: 5)
example.wrappedValue -= 10
print(example.wrappedValue) // 输出 0
2、全局/类属性的可变性
在视图中,值类型实例必须结合 SwiftUI 的 @State、@Binding 或其他可变性修饰符,才能支持在界面中动态更新。
3、使用 mutating 方法
如果需要支持通过实例的属性或方法来修改值类型,必须声明相关的属性或方法为 mutating,如:
struct NonNegative<Value: BinaryInteger> {
private var value: Value
var wrappedValue: Value {
get { value }
mutating set {
value = max(0, newValue)
}
}
}
4、通过 SwiftUI 状态管理
在 SwiftUI 中,使用状态管理修饰符(如 @State)是主流方式,这种修饰符确保实例的可变性并支持界面更新。
为什么要明确值类型的可变性?
Swift 对值类型有严格的不可变性规则是为了避免意外的副作用。值类型是值传递的(而非引用传递),默认不可变可以防止难以追踪的状态变化。