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

ReactiveSwift合并[Property<Int>]归约求和时值不更新问题

问题描述

我有一个 Property<Int> 类型的数组,需要对其执行归约操作,计算所有元素最新值的总和(实际业务场景为汇总不同SDK的未读通知计数),并将结果封装为新的 Property<Int>,我尝试了如下实现:

let unseen: Property<Int> = .init(
            initial: 0,
            then: countExtractor(counters: counters)
        )
func countExtractor(counters: [Property<Int>]) -> SignalProducer<Int?, Never> {
    SignalProducer { observer, lifetime in
        guard !lifetime.hasEnded else {
            return
        }
        let producers = counters.map { $0.producer }
        lifetime += SignalProducer<SignalProducer<Int, Never>, Never>(producers)
            .flatten(.latest)
            .reduce(0) { previous, current in
                return (previous ?? 0) + current
            }
            .start(observer)
    }
}

上述代码可正常启动运行,但最终生成的Property值不会随多个源Property的更新而同步刷新;如果硬编码仅传入单个Property,功能则完全正常。

问题根因

代码里两处核心逻辑使用错误:

  • flatten(.latest)的行为是仅保留最后一个传入内部信号的订阅,前面的信号在新信号接入时会被直接取消。传入多个Property时,除了数组最后一个元素的更新能被监听到,其余所有源的更新都会被忽略,这也是单传一个Property时功能正常的原因。
  • ReactiveSwift中的reduce算子只会在上游流完结时输出一次最终累计结果,不会在每次新值到来时输出中间累计值,完全不符合实时更新总和的需求。
修复方案

不需要手动写闭包管理订阅生命周期,直接用框架提供的combineLatest能力即可:它会同时订阅所有传入的响应式源,每次任意一个源更新时,都会把所有源的最新值拼成数组发出,直接在这个流上做求和就能实现实时汇总。

// 直接生成汇总Property,无需手动写初始值和自定义SignalProducer
let unseen: Property<Int> = Property
    .combineLatest(counters) // 同时订阅所有计数Property
    .map { counterArray in
        counterArray.reduce(0, +) // 任意计数更新时重新计算总和
    }

如果要保留原有countExtractor的封装形式,也可以直接返回合并后的流:

func countExtractor(counters: [Property<Int>]) -> SignalProducer<Int, Never> {
    guard !counters.isEmpty else { return .init(value: 0) }
    return Property.combineLatest(counters)
        .producer
        .map { $0.reduce(0, +) }
}

该实现会实时监听所有传入的Property<Int>更新,任意一个未读计数变化都会重新计算总和,同步更新unseen的值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 07:45:30