RxSwift中的Timer

我们在项目中经常会用到定时器,先来看下 swift 中使用定时器的几种方式:

Timer

//第一种写法
timer1 = Timer.init(timeInterval: 1,
                    target: self,
                    selector: #selector(timerFire),userInfo: nil,
                    repeats: true)
RunLoop.current.add(timer1, forMode: .common)

//第二种写法
timer = Timer.scheduledTimer(withTimeInterval: 1,
                            repeats: true,
                            block: { (timer) inprint("定时中。。。")
})
RunLoop.current.add(timer, forMode: .common)复制代码

这两种只是写法不同而已,第一种初始化的方式创建的 timer 需要手动添加到 runloop 中才能启动。第二种 scheduled 开头的方法,会默认把 timer 加入到当前 runloopdefault 模式下。但它们都需要改成 common 模式, runloop 才会在滚动视图时同时也响应定时器。这个 timer 的生命周期以及内存泄漏风险都需要我们自己管理。

CADisplayLink

displayLink = CADisplayLink(target: self, selector: #selector(timerFire))displayLink?.preferredFramesPerSecond = 1
displayLink?.add(to: RunLoop.current, forMode: .common)复制代码

CADisplayLinkTimer 差不多,是根据屏幕刷新频率来的,也是需要添加在 common 模式下。

DispatchSourceTimer

gcdTimer = DispatchSource.makeTimerSource()
gcdTimer?.schedule(deadline: DispatchTime.now(),
                    repeating: DispatchTimeInterval.seconds(1))
gcdTimer?.setEventHandler(handler: {print("定时中。。。")
})
gcdTimer?.resume()复制代码

CGD中的定时器在使用时,不受主线程 runloop 的影响。它会在自己所在的线程中一直执行下去,直到你 suspend 或者 cancel 掉它。而且还可以指定 handler 所执行的线程。

RxSwift的Timer

let _ = Observable.interval(RxTimeInterval.seconds(1), scheduler: MainScheduler())
    .subscribe(onNext: { (state) inprint(state)
    })
    .disposed(by: disposeBag)复制代码

首先,这个 timer 也是不用关心 runloop 方面的问题。

进去看看这个 interval 函数:

public static func interval(_ period: RxTimeInterval, 
                            scheduler: SchedulerType) -> Observable{return Timer(
        dueTime: period,
        period: period,
        scheduler: scheduler
    )
}复制代码

参数1: RxTimeInterval ,就是 DispatchTimeInterval 的别名。 public typealias RxTimeInterval = DispatchTimeInterval

参数2:调度者, MainScheduler() 的视线中能看出,我们创建的是一个主线程调度者。

public init() {
    self._mainQueue = DispatchQueue.main
    super.init(serialQueue: self._mainQueue)
}复制代码

返回值是一个初始化的 Timer 对象, timer 中保存了这些参数值,到此为止。

final private class Timer: Producer{
    fileprivate let _scheduler: SchedulerType
    fileprivate let _dueTime: RxTimeInterval
    fileprivate let _period: RxTimeInterval?

    init(dueTime: RxTimeInterval, period: RxTimeInterval?, scheduler: SchedulerType) {
        self._scheduler = scheduler
        self._dueTime = dueTime
        self._period = period
    }
}复制代码

这个 timerProducer 的子类,也是一个序列。 timer 中的泛型要求遵守 RxAbstractInteger 协议的。这个协议也是个别名, public typealias RxAbstractInteger = FixedWidthIntegerFixedWidthInteger 协议要求遵守它的实例都是用固定大小的整数类型。所以我们在创建序列的时候把泛型指定为 Int

在创建了 timer 后,我们又进行了订阅,熟悉 RxSwift核心逻辑 的话,就会清楚订阅信号后,内部会创建一个匿名的观察者,然后返回创建的销毁者中会调用 timer序列 的订阅函数,这里又根据你指定的线程在不同的分支中走了 timer序列run

func run(_ observer: Observer, 
                                cancel: Cancelable) -> (sink: Disposable, 
                                                        subscription: Disposable) {if self._period != nil {let sink = TimerSink(parent: self, observer: observer, cancel: cancel)let subscription = sink.run()return (sink: sink, subscription: subscription)
    } else {
        ......
    }
}复制代码

这里还是 RxSwift核心逻辑 熟悉的路子,创建 TimerSink 并调用 run

final private class TimerSink: Sinkwhere Observer.Element : RxAbstractInteger  {
    typealias Parent = Timerprivate let _parent: Parent
    private let _lock = RecursiveLock()

    init(parent: Parent, observer: Observer, cancel: Cancelable) {
        self._parent = parent
        super.init(observer: observer, cancel: cancel)
    }

    func run() -> Disposable {return self._parent._scheduler.schedulePeriodic(0 as Observer.Element, 
                                                        startAfter: self._parent._dueTime,  
                                                        period: self._parent._period!) 
        { state inself._lock.lock(); defer { self._lock.unlock() }
            self.forwardOn(.next(state))return state &+ 1
        }
    }
}复制代码

run 里面主要就是 schedulePeriodi ,起始时间默认为0,后面两个都是 timer序列 的间隔时间,最后还有个闭包。这个函数会在我们外面设置的那个 主线程scheduler 中调用,为了防止多线程调用导致数据错误,这里加了线程锁。 state &+ 1 也对应了 FixedWidthInteger 协议,防止溢出。继续钻进去:

MainScheduler 的父类 SerialDispatchQueueSchedulerschedulePeriodi 的实现:

public func schedulePeriodic(_ state: StateType, 
                                        startAfter: RxTimeInterval, 
                                        period: RxTimeInterval, 
                                        action: @escaping (StateType) -> StateType) -> Disposable {return self.configuration.schedulePeriodic(state, 
                                                startAfter: startAfter, 
                                                period: period, 
                                                action: action)
}复制代码

又是一个接口:

func schedulePeriodic(_ state: StateType, 
                                startAfter: RxTimeInterval, 
                                period: RxTimeInterval, 
                                action: @escaping (StateType) -> StateType) -> Disposable {let initial = DispatchTime.now() + startAfter
    var timerState = statelet timer = DispatchSource.makeTimerSource(queue: self.queue)
    timer.schedule(deadline: initial, repeating: period, leeway: self.leeway)
        
    var timerReference: DispatchSourceTimer? = timerlet cancelTimer = Disposables.create {
        timerReference?.cancel()
        timerReference = nil
    }

    timer.setEventHandler(handler: {if cancelTimer.isDisposed {return}
        timerState = action(timerState)
    })
    timer.resume()        return cancelTimer
}复制代码

看到这里一下就明白了,从刚开始的参数类型开始就一直和GCD有关联,原来就是用序列封装了 DispatchSourceTimer ,在定时器触发时发送序列的 .next 信号。在序列销毁时 canceltimer