In the development, we will often use timers, mainly with NSTimer implementation. But more or less, we have encountered some problems, such as timer failure when triggering UI operations, circular references, threads, etc. The timer failed when the UI operation was triggered because the timer was added to the runloop in the wrong mode. The problem with turning off circular references can be seen in a previous article. Of course, we can also use the GCD timer, CGD timer is more accurate, not affected by the main thread runloop. It executes in its own thread until you suspend or cancel it, and you can specify the thread on which the handler is executing. GCD timer simple and practical code:

gcdTimer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
gcdTimer.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(1))
gcdTimer.setEventHandler(handler: {
    print("gcd timer\(Thread.current)")
})
gcdTimer.resume()
Copy the code

RxSwift timer implementation

Tip: Since the core logic of RxSwift is roughly the same, the core logic of the previous article will not be repeated here, only the different points will be covered. Observable and AnonymousObservableSink RxSwift Observable and AnonymousObservableSink RxSwift Observable and AnonymousObservableSink RxSwift Observable and AnonymousObservableSink

Without further discussion, let’s take a look at how the RxSwift timer is implemented. First of all, we need to explore whether the timer of RxSwift has the following problems during its use:

  1. Whether the timer of RxSwift is acceptedrunloopThe influence of
  2. Is there a problem with circular references
  3. Whether or not you need to worry about threads

With the problem, we enter the source code analysis of RxSwift Timer

/ / parameter for the first time: the first response from the time / / the second parameter: time interval / / the third argument: thread _ = observables < Int >. The timer (DispatchTimeInterval. Seconds (0), the period: DispatchTimeInterval.seconds(1), scheduler: MainScheduler.instance).subscribe { (event)in
    print(event)
}
Copy the code

As mentioned, the RxSwift Timer is very simple to use. Swift file to find the Timer method, which is an extension of the ObservableType protocol and returns a Timer object of type Observable. So a Timer is also an observable sequence, and we can subscribe to receive messages sent by observers.

    public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval? = nil, scheduler: SchedulerType)
        -> Observable<Element> {
        return Timer(
            dueTime: dueTime,
            period: period,
            scheduler: scheduler
        )
    }
Copy the code

So let’s go back to the Timer class and see how does a Timer actually work

final private class Timer<Element: RxAbstractInteger>: Producer<Element> {
    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<Observer:  ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable)where Observer.Element == Element {
        ifself._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)
        }
    }
}
Copy the code

During initialization, three arguments passed from outside are saved. In the above example, the Timer object is created and the subscribe method is called. If we read the last two articles on the RxSwift core logic analysis, we know that after calling the SUBSCRIBE method, the code executes the subscribe method of the Producer class, and then executes the Run method of the Timer class. Create the TimerSink object in the run method, hold the Timer object, and execute sink.run, just like any other sequence. Enter the TimerSink class:

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

    private 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 in
            self._lock.lock(); defer { self._lock.unlock() }
            self.forwardOn(.next(state))
            return state &+ 1
        }
    }
}
Copy the code

_parent is the Timer object, and then has been tracking schedulePeriodic into, finally come to DispatchQueueConfiguration schedulePeriodic method of a class

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

        var timerState = state

        let timer = DispatchSource.makeTimerSource(queue: self.queue)
        timer.schedule(deadline: initial, repeating: period, leeway: self.leeway)
        
        var timerReference: DispatchSourceTimer? = timer
        letcancelTimer = Disposables.create { timerReference? .cancel() timerReference = nil } timer.setEventHandler(handler: {if cancelTimer.isDisposed {
                return
            }
            timerState = action(timerState)
        })
        timer.resume()
        
        return cancelTimer
    }
Copy the code

As you can see, the RxSwift timer is actually a encapsulated DispatchSource timer. The timer’s cancel method is executed to cancel the timer when the sequence is destroyed. The action(timerState) code block is called when the timer is triggered and the return value of the code block, state, is saved (the initial value is 0). This code block is the trailing closure of the previous call to the TimerSink class’s run method. This function is called from the main scheduler we set outside. To prevent data errors due to multi-threaded calls, a thread lock is added here. Then the forwardOn method is executed and the rest of the process is the same as the RxSwift core logic described in the previous two articles. State is a variable that follows FixedWidthInteger. State &+ 1 is a bit operation.

conclusion

To sum up the above questions:

  • Because RxSwift’s timer is encapsulatedDispatchSourceTimers are immunerunloopThe effects of
  • DispatchSourceSelf is not referenced directly, so there are no problems with circular references
  • When creating a Timer sequence, we must specify oneMainSchedulerInstance, which specifies the thread of code execution.