如何在SignalProducer终止时获取最后发送的值?
你提到的需求很常见——想在SignalProducer终止时,知道它有没有发送过值,并且拿到最后一个发送的值。你试过用局部变量存储的方法,其实这完全可行,但ReactiveSwift提供了更贴合响应式编程风格的方案,我给你整理几种不同的思路:
方案1:用collect直观获取所有值(适合值不多的场景)
collect操作符会把SignalProducer发送的所有值收集成一个数组,只有当producer正常完成时才会发送这个数组。你可以通过数组的last属性拿到最后一个值,数组为空就说明从未发送过值:
let myProducer: SignalProducer<MyObject, MyError> = getMyProducer() myProducer .collect() .on(value: { values in if let lastValue = values.last { print("最后一个发送的值:\(lastValue)") } else { print("从未发送过任何值") } }) .start()
⚠️ 注意:如果你的producer会发送大量值,这个方法会占用内存存储所有值,这时候可以看下面的方案。
方案2:用scan高效跟踪最后一个值(适合大量值的场景)
scan可以帮你累积一个状态,这里我们用它只保留最后一个发送的值,初始状态设为nil。这样每次有新值进来时,就更新这个状态,当producer终止时,就能拿到最后一个值:
let myProducer: SignalProducer<MyObject, MyError> = getMyProducer() myProducer .scan(nil as MyObject?) { _, newValue in newValue } .on(completed: { lastValue in if let value = lastValue { print("最后一个发送的值:\(value)") } else { print("从未发送过任何值") } }) .start()
这个方法只会存储最后一个值,内存效率更高,但同样只处理正常完成的情况,如果producer是因为错误终止,completed回调不会触发。
方案3:用materialize处理所有终止情况(包括错误)
如果你的producer可能因为错误终止,而你想在任何终止场景下都检查最后一个值,materialize是更好的选择。它会把所有事件(Next/Completed/Failed)转换成可观察的事件对象,你可以在收到Completed或Failed事件时,检查之前记录的最后一个值:
let myProducer: SignalProducer<MyObject, MyError> = getMyProducer() var lastValue: MyObject? myProducer .materialize() .on(value: { event in switch event { case let .next(value): lastValue = value // 记录每次发送的值 case .completed, .failed: // 不管是完成还是出错,都检查最后一个值 if let value = lastValue { print("终止时最后一个值:\(value)") } else { print("从未发送过任何值") } } }) .start()
这个方法兼顾了正常完成和错误终止的情况,和你最开始用局部变量的思路类似,但用响应式的方式处理了事件流。
关于你最初的局部变量方案
其实你最开始的思路是没问题的,只要注意线程安全(如果producer在后台线程发送值,建议用Atomic或者在同一个调度队列里访问变量),比如:
import ReactiveSwift let myProducer: SignalProducer<MyObject, MyError> = getMyProducer() let lastValue = Atomic<MyObject?>(nil) myProducer .on( value: { value in lastValue.value = value }, terminated: { if let value = lastValue.value { print("最后一个值:\(value)") } else { print("从未发送过值") } } ) .start()
用ReactiveSwift的Atomic可以保证多线程下的线程安全,避免竞态条件。
内容的提问来源于stack exchange,提问作者batsansierra

