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
相关产品推荐
相关产品推荐

