【问题标题】:Cannot use mutating getter on immutable value: 'self' is immutable error不能对不可变值使用变异 getter:'self' 是不可变错误
【发布时间】:2026-01-26 02:50:01
【问题描述】:

我正在尝试重用一段较旧的 Swift 代码,但出现错误“不能在不可变值上使用 mutating getter:'self' is immutable error”。 Xcode 想在 func 之前添加“变异”,并提出通过“修复”来实现。所以错误消失了,但仍然存在于“文本”语句中。

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

【问题讨论】:

  • 我发现这个问题的解决方法是使用计算属性而不是惰性变量。我不明白为什么我不能使用惰性变量,但我也不明白建议的解决方案
  • @charel-f 我写了一个新答案。请看一下
  • 谢谢,您的新答案非常简洁易懂

标签: swift swiftui getter-setter getter computed-properties


【解决方案1】:

因为当您在结构内部调用x 时,不清楚contentView 本身是否可变。一个值类型只有在被定义为var时才变得可变。

因此,如果在结构内部的构建器函数中使用它之前使用不可变值包装它会有所帮助。

像这样:

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

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

?注意:延迟属性的值将在第一次调用时关联,这会改变对象。所以他们被认为是mutating

【讨论】:

    【解决方案2】:

    这与SwiftUI 无关。这是关于 Swift 使用它的 getter 执行的设计。原理是:

    getter 不应该改变对象。因为开发人员可能没有预料到这一点。当您使用 setter 或调用 mutating 函数时,他们应该只期待更改。吸气剂都不是。

    以下示例按预期工作:

    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.
    

    【讨论】:

    • 这帮助我意识到惰性属性在第一次调用时会改变对象,因此将始终被视为mutating。这很不幸,但至少现在说得通。