使用@ObservedObject与@Published时视图提前刷新的问题咨询
SwiftUI中@ObservedObject与@Published的刷新时机问题解析
你遇到的问题核心是对@Published的触发时序理解有偏差,这是很常见的认知误区:
@Published的默认触发逻辑
@Published底层是在属性即将变更前发送objectWillChange通知,这是Combine框架的设计特性。当SwiftUI视图收到这个通知后会立刻开始刷新,而此时属性的新值还没完成赋值,所以视图读取到的自然是旧值。
手动调用objectWillChange的差异
当你移除@Published,改用didSet里调用objectWillChange.send()时,通知是在属性值已经完全更新后发送的。视图收到通知再刷新时,新值已经就位,所以能拿到正确的结果,这符合你预期的“变更后刷新”逻辑。
纠正认知误区
很多开发者会默认认为@Published是在属性变更后触发视图刷新,但实际上它的设计更偏向Combine数据流的“即将变更”通知,而非SwiftUI视图刷新的“已变更”时机。这种设计在某些Combine场景下有用,但在简单的视图刷新场景就会出现时序问题。
可行的解决方案
- 如果你需要稳定的“变更后刷新”,直接用
didSet + objectWillChange.send()的写法替代@Published - 若必须保留@Published,可以在视图中通过
onReceive监听属性的Combine发布者,确保拿到新值后再处理视图逻辑,比如:.onReceive(vm.$count) { newCount in // 这里拿到的是新值,可用于更新视图状态 } - 如果你适配iOS 17+/macOS 14+及以上系统,推荐使用新的
@Observable宏替代ObservableObject和@Published,它的默认刷新时机是属性变更后,逻辑更直观,也能避免这类时序问题
代码与日志对比示例
原@Published代码
class MyViewModel: ObservableObject { @Published var count = 0 func increment() { print("准备修改count为\(count+1)") count += 1 print("count已修改为\(count)") } } struct ContentView: View { @ObservedObject var vm = MyViewModel() var body: some View { VStack { Text("当前count: \(vm.count)") Button("点击") { vm.increment() } } .onAppear { print("视图刷新,当前count: \(vm.count)") } } }
日志输出:
准备修改count为1
视图刷新,当前count: 0
count已修改为1
修改后didSet代码
class MyViewModel: ObservableObject { var count = 0 { didSet { objectWillChange.send() } } func increment() { print("准备修改count为\(count+1)") count += 1 print("count已修改为\(count)") } }
日志输出:
准备修改count为1
count已修改为1
视图刷新,当前count: 1
内容的提问来源于stack exchange,提问作者Vincent
相关产品推荐
相关产品推荐

