我试图重用一段旧的 Swift 代码,但收到错误“无法在不可变值上使用变异 getter:'self' 是不可变错误”。 Xcode 希望在 func 之前添加“mutating”,并提出通过“fix”来实现。因此错误已消失,但仍保留在“文本”语句中。
import SwiftUI
struct ContentView: View {
typealias PointTuple = (day: Double, mW: Double)
let points: [PointTuple] = [(0.0, 31.98), (1.0, 31.89), (2.0, 31.77), (4.0, 31.58), (6.0, 31.46)]
lazy var meanDays = points.reduce(0) { $0 + $1.0 } / Double(points.count)
lazy var meanMW = points.reduce(0) { $0 + $1.1 } / Double(points.count)
lazy var a = points.reduce(0) { $0 + ($1.day - meanDays) * ($1.mW - meanMW) }
lazy var b = points.reduce(0) { $0 + pow($1.day - meanDays, 2) }
lazy var m = a / b
lazy var c = meanMW - m * meanDays
lazy var x : Double = bG(day: 3.0)
lazy var y : Double = bG(day: 5.0)
lazy var z : Double = bG(day: 7.0)
mutating func bG(day: Double) -> Double {
return m * day + c
}
var body: some View {
VStack {
Text("\(x)")
Text("\(y)")
Text("\(z)")
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
因为当你在结构体内部调用
x
时,并不清楚 contentView
本身是否可变。值类型仅当被定义为var
时才可变。
因此,如果在结构体内部的构建器函数中使用它之前用不可变值包装它,将会有所帮助。
像这样:
func xValue() -> Double {
var mutatableSelf = self
return mutatableSelf.x
}
var body: some View {
VStack {
Text("\(xValue())")
}
}
💡注意:
Lazy
属性的值将在第一次调用时关联,这会改变对象。所以他们被认为是 mutating
这主要是 Swift 通过其吸气剂强制执行的设计。原理是:
吸气剂不应该改变对象。因为开发人员可能没有预料到这一点。仅当您使用 setter 或调用 mutating 函数时,他们才应该期待更改。 getter 都不是它们。
以下示例按预期工作:
struct Device {
var isOn = true
}
let a = Device()
let b = a
print(a.isOn)
当我们打印时,我们得到
a.isOn
的值。 a,b 都是相同的。
但是在下面的例子中,getter 会产生副作用。它甚至不会编译。但我们假设它确实发生了,看看会发生什么。
struct Device2 {
var x = 3
var isOn: Bool {
x = 5
return true
}
}
let a = Device2()
let b = a
print(a.isOn)
当我们打印时,我们得到
a.isOn
的值。然而这一次 a、b 不再相同。 a.x
现在将为 5,因为会发生写时复制,而 b.x
仍为 3。
Swift 的架构不允许“getters 改变对象”。
Swift 架构对此规则有两个例外:
lazy
@State
或由 SwiftUI 管理的其他一些属性包装器,可以写入和管理状态。struct Device2 {
lazy var x : Double = 3.0
func log() {
print(x)
}
}
即使认为
print(x)
会在获取
x
的值时改变x
,也没关系,因为x
是一个lazy
属性。
您可能想知道
lazy var x = 5
和 var x = 5
之间有什么不同,答案是后者在结构初始化时设置了 x
的值...
由于
body
变量是计算属性,因此您无法更改/设置变量。不过有办法解决这个问题。
使用
@State
属性包装器标记变量。
示例。以下代码无法编译:
struct ContentView: View {
var currentDate = Date()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text("\(currentDate)")
.onReceive(timer) { input in
currentDate = input // ERROR: Cannot assign to property: 'self' is immutable
}
}
}
但是下面的代码会编译,因为它有
@State
struct ContentView: View {
@State var currentDate = Date()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
Text("\(currentDate)")
.onReceive(timer) { input in
currentDate = input
}
}
}
有关更多信息,请参阅此处