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

SwiftUI监听/观察异常:Class类型数据无法更新UI,Struct正常

问题根因

问题出在引用类型与值类型的特性差异:

  • 当Lobby是Struct(值类型)时,修改users属性会生成全新的Lobby实例,ViewModel中@Published var currentLobby能检测到实例变化,自动通知UI更新。
  • 当Lobby是Class(引用类型)时,修改currentLobby?.users只是修改了引用指向对象的内部数据,currentLobby这个引用本身没有变化,因此@Published不会发出更新通知,UI自然无反应。
解决方案

方案1:手动触发ViewModel更新通知

在修改users后,手动调用ViewModel的objectWillChange.send(),强制通知SwiftUI刷新UI:

DispatchQueue.main.async {
    self.currentLobby?.users = users
    // 手动触发UI更新
    self.objectWillChange.send()
    print("Lobby Users: \(self.currentLobby?.users)")
}

方案2:让ViewModel监听Lobby内部变化

利用Lobby本身的ObservableObject特性,在ViewModel中订阅它的变化事件并转发:

@MainActor
final class LobbyViewModel : ObservableObject {
    @Published var currentLobby: Lobby? = nil
    @Published var userListener: ListenerRegistration?
    @Published var lobbyListener: ListenerRegistration?
    var live: Bool = true
    var userActive = true
    var lobbyActive = true
    // 用于存储订阅对象,防止被释放
    private var cancellables = Set<AnyCancellable>()

    // 替换直接赋值currentLobby的逻辑,改用此方法
    func updateCurrentLobby(_ newLobby: Lobby?) {
        currentLobby = newLobby
        // 清空旧订阅
        cancellables.removeAll()
        // 订阅Lobby内部变化,转发给ViewModel的更新通知
        currentLobby?.objectWillChange
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.objectWillChange.send()
            }
            .store(in: &cancellables)
    }
}

后续获取到新的Lobby实例时,调用updateCurrentLobby方法而非直接赋值,这样Lobby内部属性的变化就能触发UI更新。

方案3:改用Struct(业务允许的话)

如果Lobby不需要作为引用类型共享,直接改成Struct是最省心的方式——值类型的属性修改会自动触发@Published的更新通知,无需额外处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 12:02:49