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

如何在Combine中最优创建@Published值的聚合发布者

解决方案

核心问题分析

  • 取值错误的根源是@Published的默认发布时机是属性的willSet阶段:属性值即将变更但还未完成赋值时就发出事件,此时访问实例属性拿到的还是旧值
  • 原有实现嵌套多层switchToLatest逻辑冗余,可通过单次扁平映射完成需求

最优实现代码

import Combine

class Foo: ObservableObject {
    @Published var bar: Int = 0
}

class Foobar: ObservableObject {
    @Published var foo: [Foo] = []
    
    var sumPublisher: AnyPublisher<Int, Never> {
        $foo
            // 跳过willSet阶段,等属性赋值完成后再执行后续逻辑,保证拿到最新值
            .receive(on: DispatchQueue.main)
            // 每次数组变更,切换到新数组内所有bar属性的变更监听
            .flatMapLatest { latestFooArray -> AnyPublisher<Int, Never> in
                // 合并数组内所有Foo实例的bar变更发布者
                return Publishers.MergeMany(latestFooArray.map { $0.$bar })
                    // 任何bar变更时,用最新数组计算总和
                    .map { _ in
                        latestFooArray.reduce(0) { $0 + $1.bar }
                    }
                    // 数组刚变更时立刻输出当前总和,不需要等待bar变更
                    .prepend(latestFooArray.reduce(0) { $0 + $1.bar })
                    .eraseToAnyPublisher()
            }
            .removeDuplicates()
            .eraseToAnyPublisher()
    }
}

// 低版本Combine不支持flatMapLatest的话可以替换为如下写法
/*
var sumPublisher: AnyPublisher<Int, Never> {
    $foo
        .receive(on: DispatchQueue.main)
        .map { latestFooArray in
            Publishers.MergeMany(latestFooArray.map { $0.$bar })
                .map { _ in
                    latestFooArray.reduce(0) { $0 + $1.bar }
                }
                .prepend(latestFooArray.reduce(0) { $0 + $1.bar })
        }
        .switchToLatest()
        .removeDuplicates()
        .eraseToAnyPublisher()
}
*/

方案说明

  • 完全基于@Published原生能力实现,输出时机和SwiftUI视图更新完全同步,满足使用要求
  • 仅用单层映射逻辑,比原有实现精简70%以上
  • 自动处理数组增删元素、数组内元素属性变更两种场景,所有场景下取值都是最新值
  • 聚合逻辑可随意替换,只需要修改reduce部分的代码即可实现任意复杂的聚合计算
  • 无循环引用风险

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 13:45:02