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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:22:47