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

qv7cva1a  于 2023-01-12  发布在  Swift
关注(0)|答案(2)|浏览(133)

我尝试重用Swift的一段旧代码,但是得到一个错误“不能在不可变值上使用mutating 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) { $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
zyfwsgd6

zyfwsgd61#

因为当你在struct内部调用x时,并不清楚contentView本身是否是可变的,一个值类型只有在被定义为var时才是可变的。
因此,如果在结构体内部的构建器函数中使用它之前,先用一个不可变的值 Package 它,那会很有帮助。
像这样:

func xValue() -> Double {
    var mutatableSelf = self
    return mutatableSelf.x
}

var body: some View {
    VStack {
        Text("\(xValue())")
    }
}

💡* * 注意**:Lazy属性的值将在第一次调用时关联,这会使对象发生变化。因此,它们被视为mutating

kfgdxczn

kfgdxczn2#

getter无法变异

这主要是Swift在getter中实施的一种设计,其原理是:
getter不应该改变对象,因为开发者可能不希望这样,他们应该只在你使用 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的架构不允许“getter改变对象”。

例外情况

Swift体系结构对此规则有两个例外:

  • 使用lazy
  • 使用@State或其他属性 Package

Lazy正在变异:

struct Device2 {
    lazy var x : Double = 3.0

    func log() {
        print(x)
    } 
}

即使print(x)在 * 获得 * x的值时会改变x,这也没关系,因为它是一个惰性属性。
您可能想知道lazy var x = 5var x = 5之间有什么不同,答案是后者在结构体初始化时设置了x的值...

SwiftUI -特殊情况

因为body变量是一个计算属性,所以你不能改变/设置变量,但是有一种方法可以解决这个问题。
使用@State属性 Package 器标记变量。
下面的代码将无法编译:

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

相关问题