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
相关产品推荐
相关产品推荐

