无法对不可变值使用变异 getter:'self' 是不可变错误

Cannot use mutating getter on immutable value: 'self' is immutable error

我正在尝试重用一段较旧的 Swift 代码,但出现错误“无法对不可变值使用变异 getter:'self' 是不可变错误”。 Xcode 想在 func 之前添加 'mutating',并提出通过 'fix' 来实现。所以错误在那里消失了,但仍然存在于 'Text' 语句中。

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) { [=12=] + .0 } / Double(points.count)
    lazy var meanMW   = points.reduce(0) { [=12=] + .1 } / Double(points.count)

    lazy var a = points.reduce(0) { [=12=] + (.day - meanDays) * (.mW - meanMW) }
    lazy var b = points.reduce(0) { [=12=] + pow(.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

一个getter不能变异

这主要是 Swift 的 getter 设计。原则是:

getter 不应改变对象。因为开发人员可能没有预料到这一点。他们应该只在您使用 setter 或调用 mutating 函数时才会发生变化。 A getter 两者都不是。

以下示例按预期工作:

struct Device {
    var isOn = true
}

let x = Device()
let y = Device()

y.isOn // Doing such will not cause the object to mutate.

然而下面的例子,getter会有副作用。 Swift 架构不允许这样做。

struct Device2 {
    
    var x = 3
    var isOn: Bool {
        x = 5
        return true
    }
}

let a = Device2()
let b = Device2()

a.isOn // Doing such will mutate the object. a.x will be '5'. While `b.x` will be '3'. Swift doesn't want to allow this.

懒惰正在变异:

struct ContentView: View {
    lazy var x : Double = 3.0

    var body: some View {
       Text("\(x)") // ERROR
    }
}

将导致以下错误:

Cannot use mutating getter on immutable value: 'self' is immutable

SwiftUI - 特例

因为 body 变量是一个计算的 属性,你不能 mutate/set 变量。不过有办法解决这个问题。

@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
        }
    }
}

有关更多信息,请参阅 here