使用Combine观察Singleton时.sink未被调用的问题
问题:Combine观察单例对象属性变化无响应
问题描述
使用Combine监听单例MREUserService的ownUser变化时,sink闭包始终不触发。预期每隔3秒打印“MODDED”后会打印“CHECK”,但实际仅输出“MODDED”。已尝试将单例设为@ObservableObject并手动调用objectWillChange.send(),但问题仍未解决。
原因分析
核心问题在于@Published的触发机制:
@Published仅当被包装的对象引用发生变更时,才会向数据流发送新事件。- 当前代码中只是修改了
ownUser的userName属性,ownUser本身的引用没有改变,因此MREUserService.shared.$ownUser的数据流不会发出新值,导致sink闭包无法执行。 - 手动调用
objectWillChange.send()对@Published的数据流无效,因为@Published有独立的事件发布逻辑。
解决方案
提供两种可行的修复方案,可根据实际场景选择:
方案一:让MREUser成为可观察对象
将MREUser改为ObservableObject,并把userName标记为@Published,这样属性变化时能主动发出通知,同时调整监听逻辑来监听内部属性变化。
方案二:替换整个ownUser对象
如果不需要MREUser本身是可观察对象,可在修改属性时直接替换整个ownUser实例,让@Published检测到引用变化并触发事件。
完整修正代码
方案一代码
class MREViewModel: ObservableObject { private var cancellables = Set<AnyCancellable>() @Published var ownUser: MREUser init() { ownUser = MREUserService.shared.ownUser setupBindings() } func setupBindings() { // 监听ownUser的userName属性变化 MREUserService.shared.$ownUser .flatMap { $0.$userName } .receive(on: DispatchQueue.main) .sink { [weak self] _ in print("CHECK") // 因为ownUser引用未变,这里只需更新本地属性或直接触发视图刷新 self?.objectWillChange.send() } .store(in: &cancellables) } } struct ViewSwiftUI: View { @ObservedObject var viewModel: MREViewModel var body: some View { Text(viewModel.ownUser.userName) } } class MREUserService: ObservableObject { static var shared: MREUserService = MREUserService() @Published var ownUser = MREUser(userName: "initialised") var timer: Timer? init() { timer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: true) { timer in self.ownUser.userName = "\(Date())" print("MODDED: \(self.ownUser.userName)") } } } // 修改MREUser为ObservableObject class MREUser: ObservableObject { var _id = UUID().uuidString @Published var userName: String init(userName: String) { self.userName = userName } }
方案二代码
class MREViewModel: ObservableObject { private var cancellables = Set<AnyCancellable>() @Published var ownUser: MREUser init() { ownUser = MREUserService.shared.ownUser setupBindings() } func setupBindings() { MREUserService.shared.$ownUser .receive(on: DispatchQueue.main) .sink { [weak self] user in print("CHECK") self?.ownUser = user } .store(in: &cancellables) } } struct ViewSwiftUI: View { @ObservedObject var viewModel: MREViewModel var body: some View { Text(viewModel.ownUser.userName) } } class MREUserService: ObservableObject { static var shared: MREUserService = MREUserService() @Published var ownUser = MREUser(userName: "initialised") var timer: Timer? init() { timer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: true) { timer in // 替换整个ownUser对象,让@Published检测到引用变化 self.ownUser = MREUser(userName: "\(Date())") print("MODDED: \(self.ownUser.userName)") } } } class MREUser { var _id = UUID().uuidString var userName: String init(userName: String) { self.userName = userName } }
内容的提问来源于stack exchange,提问作者ambassador
相关产品推荐
相关产品推荐

