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:
- Whether the timer of RxSwift is accepted
runloop
The influence of - Is there a problem with circular references
- 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 encapsulated
DispatchSource
Timers are immunerunloop
The effects of DispatchSource
Self is not referenced directly, so there are no problems with circular references- When creating a Timer sequence, we must specify one
MainScheduler
Instance, which specifies the thread of code execution.