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

问题描述 投票:0回答:2

我试图重用一段旧的 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
swift swiftui getter-setter getter computed-properties
2个回答
26
投票

因为当你在结构体内部调用

x
时,并不清楚
contentView
本身是否可变。值类型仅当被定义为
var
时才可变。

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

像这样:

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

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

💡注意

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


24
投票

吸气剂不能变异

这主要是 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
的值...

SwiftUI - 特殊情况

由于

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

有关更多信息,请参阅此处

© www.soinside.com 2019 - 2024. All rights reserved.