You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中使用Class与Struct时@Published属性不更新视图问题

SwiftUI中Class与Struct的视图更新差异问题解析

问题现象

在你的代码中:

  • 修改SampleTimeCounterStruct(值类型)的showerTimeMinutes属性时,视图会正常刷新
  • 修改SampleTimeCounter(普通引用类型)的属性时,视图无任何变化
  • 即使SampleTimeCounterPublihser遵循了ObservableObject且内部属性用@Published修饰,修改它的属性时视图依然不更新

原因分析

1. Struct(值类型)的更新逻辑

@Published修饰值类型时,只要属性内容发生修改,就会触发整个值的替换操作(值类型的修改本质是创建新实例)。@Published会检测到这个实例替换,自动发送更新通知,驱动视图刷新。

2. 普通Class(引用类型)的更新逻辑

@Published修饰引用类型时,只会监听引用本身的变化(比如给timeCounter重新赋值一个新的SampleTimeCounter实例)。仅仅修改Class内部的属性,引用指向的内存地址没有改变,@Published不会感知到变化,自然不会触发视图更新。

3. 嵌套ObservableObject的更新逻辑

SampleTimeCounterPublihser自身是ObservableObject,内部的@Published会发送更新通知,但这个通知无法自动传递到外层的SampleViewModel。外层@Published依然只监听引用的变化,所以内部属性修改时,外层ViewModel不会发送更新信号给视图。

解决方案

针对普通Class的解决办法

方案1:将Class改为Struct(优先推荐,若无需引用语义)

如果不需要共享实例或引用语义,直接把SampleTimeCounter改成Struct,就能像SampleTimeCounterStruct一样自动触发视图更新:

struct SampleTimeCounter {
    var showerTimeMinutes = "00"
}

方案2:手动触发ViewModel的更新

如果必须保留Class类型,修改内部属性后手动调用ViewModel的objectWillChange.send(),强制发送更新通知:

// 在SampleViewModel中添加方法
class SampleViewModel: ObservableObject {
    @Published var timeCounter = SampleTimeCounter()
    // ... 其他属性 ...

    func updateClassMinutes(_ value: String) {
        timeCounter.showerTimeMinutes = value
        objectWillChange.send()
    }
}

// 在视图中调用这个方法
Button("Update Minutes Class") {
    viewModel.updateClassMinutes("15")
}

针对嵌套ObservableObject的解决办法

方案1:转发内部ObservableObject的更新通知

在SampleViewModel的初始化方法中,订阅嵌套对象的objectWillChange事件,然后转发给自己的objectWillChange:

class SampleViewModel: ObservableObject {
    @Published var timeCounter = SampleTimeCounter()
    @Published var timeCounterStruct = SampleTimeCounterStruct()
    @Published var timerCounterPublisher = SampleTimeCounterPublihser()
    
    init() {
        // 订阅嵌套对象的更新事件并转发
        _ = timerCounterPublisher.objectWillChange
            .sink(receiveValue: { [weak self] _ in
                self?.objectWillChange.send()
            })
    }
}

方案2:在视图中直接持有嵌套ObservableObject

在视图中用@ObservedObject直接持有嵌套的实例,跳过外层ViewModel的@Published,让视图直接监听内部对象的更新:

struct SimpleView: View {
    @StateObject private var viewModel = SampleViewModel()
    // 直接持有嵌套的ObservableObject
    @ObservedObject private var nestedCounter: SampleTimeCounterPublihser { viewModel.timerCounterPublisher }

    var body: some View {
        VStack {
            // ... 其他代码 ...
            
            VStack {
                Text(nestedCounter.showerTimeMinutes)
                Button("Update Minutes Class with Publisher") {
                    nestedCounter.showerTimeMinutes = "15"
                }
            }
        }
    }
}

总结

SwiftUI的视图更新依赖于ObservableObject发送的objectWillChange通知,而@Published对值类型和引用类型的监听逻辑不同:

  • 值类型:修改内容=实例替换,自动触发更新
  • 引用类型:仅当引用本身变化时触发更新,内部属性修改需手动转发或直接监听

内容的提问来源于stack exchange,提问作者Qazi Ammar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 14:32:17