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

Combine框架@Published属性潜在竞态问题及最佳方案咨询

@Published属性更新的竞态问题及解决最佳实践

问题根源:跨线程内存可见性竞态

你遇到的确实是竞态条件导致的异常。原因在于:

  • fetchAllNotifications里的网络请求在异步Task的后台队列执行,赋值notifications = ...也发生在后台线程。
  • @Published的机制是赋值完成后立即向订阅者发送新值,且你通过receive(on: DispatchQueue.main)把回调切换到了主队列。但此时主队列线程的内存缓存可能还没同步到viewModel.notifications的最新值——也就是说,sink回调拿到的notifs是@Published确保的最新数据,但直接访问viewModel.notifications时,主队列还没读取到后台线程更新后的属性值,就出现了两者count不一致的情况。
  • 你的表格视图大概率直接以viewModel.notifications作为数据源,此时调用reloadData()会读取到旧的空数组,导致表格显示为空。

最佳实践

方案1:确保ViewModel属性的赋值在主队列执行

把网络请求的结果切换到主队列后再赋值,让属性更新和UI回调处于同一个队列,彻底消除竞态:

func fetchAllNotifications() {
    Task {
        do {
            let fetchedNotifications = try await NotificationsService.shared.getAllNotifications()
            // 切换到主Actor上下文赋值,符合Swift Concurrency规范
            await MainActor.run {
                self.notifications = fetchedNotifications
            }
        } catch {
            printError(error)
        }
    }
}

方案2:直接使用sink回调的参数作为UI数据源

在ViewController中维护本地的数据源副本,完全依赖@Published发送的新值更新UI,不再直接访问ViewModel的属性:

class NotificationsViewController: UIViewController {
    private let viewModel = NotificationsViewModel()
    private var currentNotifications = [NotificationData]() // 本地数据源
    private var cancellables = Set<AnyCancellable>()

    // ... 其他UI配置代码

    func bindToViewModel() {
        viewModel.fetchAllNotifications()
        viewModel.$notifications
            .receive(on: DispatchQueue.main)
            .sink { [weak self] newNotifications in
                self?.currentNotifications = newNotifications
                self?.tableView.reloadData()
            }
            .store(in: &cancellables)
    }

    // 表格数据源方法使用本地副本
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return currentNotifications.count
    }
}

这种方式完全遵循Combine的设计意图:订阅者只通过回调接收最新状态,不依赖对发布者原始属性的直接访问,从根源上避免了内存可见性问题。

不推荐的方案

你提到的didSet手动通知或延迟调用reloadData()属于临时 workaround,没有解决本质的竞态问题,反而增加了代码复杂度,不建议使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:33:11