RxSwift之Timer使用探究

1,397 阅读4分钟

RxSwift之Timer使用探究

复习

作为swift的初学者,说实话对于swift的使用还是比较欠缺的,日常开发使用的机会比较少,也就最近刚开始看了一部分,所以对于一些基础的swift的使用还是有必要的联系的,写博客也是一个机会

swift中的timer的使用

// normal写法 需要手动添加到loop
let timer_1 = Timer.init(timeInterval: 1, target: self, selector: #selector(runTimer1) , userInfo: nil, repeats: true)

RunLoop.current.add(timer_1, forMode: .common)

// block写法 需要手动添加到loop
let timer_2 = Timer.init(timeInterval: 1, repeats: true) { (timer) in
    print("timer2 runing \(timer)")
}
RunLoop.current.add(timer_2, forMode: .common)

// 默认添加到loop中
_ = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (Timer) in
    print("timer3 runing \(Timer)")
}

// GCD 写法
let gcdTimer = DispatchSource.makeTimerSource()
gcdTimer.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(2))
gcdTimer.setEventHandler {
    print("gcd timer running...")
}
gcdTimer.resume()

// CADisplayLink

let displayTimer = CADisplayLink(target: self, selector: #selector(displayTimerRun))
displayTimer.preferredFramesPerSecond = 1
displayTimer.add(to: RunLoop.current, forMode: .common)

RxSwift中的定时器的使用

// Rxswift
   let _ = Observable<Int>.interval(1, scheduler: MainScheduler())
       .subscribe(
           onNext: { (count) in
           print("定阅RxSwift \(count)")
       }, onError: { (error) in
           print("错误 RxSwift \(error)")
       }, onCompleted: {
           print("定阅RxSwift finish")
       })
       .disposed(by: disposeBag)

代码分析 Rxswift的定时器的使用可以简单分析,里边肯定含有一个RxSwift自己的定时器,这个定时器内部肯定也维护了相关的loop的调度,并且在其中触发生成序列,然后触发我们定阅的相关事件

RxSwift的定时器代码调用

public static func interval(_ period: RxTimeInterval, scheduler: SchedulerType)
        -> Observable<E> {
        return Timer(
            dueTime: period,
            period: period,
            scheduler: scheduler
        )
    }

由此可见Rxswift的定时器是一个返回序列,这个返回的序列供外部的subscribe定阅 进一步分析Timer的实现和代码结构

final private class Timer<E: RxAbstractInteger>: Producer<E> {
    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
    }
    override func run<O: ObserverType>(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E {
        if self._period != nil {
            let sink = TimerSink(parent: self, observer: observer, cancel: cancel)
            let subscription = sink.run()
            return (sink: sink, subscription: subscription)
        }
        else {
            let sink = TimerOneOffSink(parent: self, observer: observer, cancel: cancel)
            let subscription = sink.run()
            return (sink: sink, subscription: subscription)
        }
    }
}   

由以上代码易知,Timer是继承自Producer的序列,这是创建可观察序列的过程。Timer也是遵循泛型E的协议,泛型ERxAbstractInteger,它是FixedWidthInteger的别名,FixedWidthInteger顾名思义固定宽度的整数,我的理解就是相邻整数之间的是有固定宽度的

下面的订阅和RxSwift的核心逻辑相似

在订阅后都会走到对应类型的run方法,此时timer中的run方法如下:


override func run<O: ObserverType>(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == E {
   if self._period != nil {
       let sink = TimerSink(parent: self, observer: observer, cancel: cancel)
       let subscription = sink.run()
       return (sink: sink, subscription: subscription)
   }
   else {
       let sink = TimerOneOffSink(parent: self, observer: observer, cancel: cancel)
       let subscription = sink.run()
       return (sink: sink, subscription: subscription)
   }
}

这里的逻辑页和Rxswift核心逻辑相似,只不过是生成了TimerSink或者TimerOneOffSink两种sink,sink会调用他们的run,以TimerSink为例:


final private class TimerSink<O: ObserverType> : Sink<O> where O.E : RxAbstractInteger  {
    typealias Parent = Timer<O.E>

    private let _parent: Parent
    private let _lock = RecursiveLock()

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

    func run() -> Disposable {
        return self._parent._scheduler.schedulePeriodic(0 as O.E, startAfter: self._parent._dueTime, period: self._parent._period!) { state in
            self._lock.lock(); defer { self._lock.unlock() }
            self.forwardOn(.next(state))
            return state &+ 1
        }
    }
}

分析run中的逻辑是取到Timer中的调度器执行schedulePeriodic,这个方法中的参数是两个时间间隔,和一个逃逸尾随闭包,这个方法是在MainScheduler的父类SerialDispatchQueueScheduler实现的,细节如下


/**
    Schedules a periodic piece of work.
    
    - parameter state: State passed to the action to be executed.
    - parameter startAfter: Period after which initial work should be run.
    - parameter period: Period for running the work periodically.
    - parameter action: Action to be executed.
    - returns: The disposable object used to cancel the scheduled action (best effort).
*/
public func schedulePeriodic<StateType>(_ state: StateType, startAfter: TimeInterval, period: TimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
   return self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)
}

进一步进入


func schedulePeriodic<StateType>(_ state: StateType, startAfter: TimeInterval, period: TimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
   let initial = DispatchTime.now() + dispatchInterval(startAfter)

   var timerState = state

   let timer = DispatchSource.makeTimerSource(queue: self.queue)
   timer.schedule(deadline: initial, repeating: dispatchInterval(period), leeway: self.leeway)
   
   // TODO:
   // This looks horrible, and yes, it is.
   // It looks like Apple has made a conceputal change here, and I'm unsure why.
   // Need more info on this.
   // It looks like just setting timer to fire and not holding a reference to it
   // until deadline causes timer cancellation.
   var timerReference: DispatchSourceTimer? = timer
   let cancelTimer = Disposables.create {
       timerReference?.cancel()
       timerReference = nil
   }

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

这里清晰的看到内部有一个GCD定时器,不断的将计时器相关的操作通过action传递出去给订阅者。