如何用CurrentValueSubject从Timer Publisher获取上一日期并简化代码
使用CurrentValueSubject简化定时器时间差计算代码
方案一:用CurrentValueSubject替代显式存储变量
通过CurrentValueSubject可以直接维护并获取上一次的日期值,无需单独声明lastSampleUpdate变量,修改后的代码如下:
import Combine var sampleTimer: AnyCancellable? // 用CurrentValueSubject持久化当前日期,替代lastSampleUpdate private let lastDateSubject = CurrentValueSubject<Date, Never>(Date()) func startSampling(on date: Date) { // 初始化subject的初始值为传入的起始日期 lastDateSubject.send(date) sampleTimer = Timer.publish(every: 20, on: .main, in: .default) .autoconnect() .receive(on: RunLoop.main) .sink { newDate in // 从subject获取上一次的日期值 let timeDifference = newDate.timeIntervalSince(lastDateSubject.value) print("\(timeDifference) seconds passed") // 更新subject的当前值为新日期 lastDateSubject.send(newDate) } }
关键改动说明
- 移除了原有的
lastSampleUpdate变量,改用CurrentValueSubject<Date, Never>来存储当前日期:它既可以保存当前值,也能作为数据流的载体,符合Combine响应式编程的思路。 - 在
startSampling方法中,先通过send(_:)将传入的起始日期设置为subject的初始值。 - 定时器的
sink闭包直接处理时间差计算和值更新:从lastDateSubject.value拿到上一次的日期,计算差值后再更新subject的当前值,替代了原有的newDate函数。
方案二:用scan操作符实现(无需额外Subject)
如果不想引入CurrentValueSubject,也可以用scan操作符直接在数据流中累积上一次的日期值,同样能移除显式变量:
import Combine var sampleTimer: AnyCancellable? func startSampling(on date: Date) { sampleTimer = Timer.publish(every: 20, on: .main, in: .default) .autoconnect() .receive(on: RunLoop.main) // scan操作符会累积上一次的输出值,初始值为传入的date .scan(date) { lastDate, newDate in let timeDifference = newDate.timeIntervalSince(lastDate) print("\(timeDifference) seconds passed") return newDate } .sink(receiveValue: { _ in }) }
scan会在每次收到新的日期时,结合上一次累积的值执行闭包逻辑,最后返回新的累积值,全程无需额外存储变量。
内容的提问来源于stack exchange,提问作者Daniel K
相关产品推荐
相关产品推荐

