在開發中,咱們會常常用到定時器,主要是用NSTimer
實現的。但或多或少都遇到過一些問題,好比說觸發UI操做時定時器失效,循環引用、線程等問題。觸發UI操做時定時器失效是由於把timer
加入到runloop
的模式不對,關閉循環引用的問題能夠看以前寫的一篇文章防止NSTimer循環引用的幾個方法。固然咱們也能夠用GCD的定時器,CGD中的定時器計時更加精準,不受主線程runloop
的影響。它會在本身所在的線程中一直執行下去,直到你suspend或者cancel掉它,並且還能夠指定handler所執行的線程。GCD定時器簡單實用的代碼:html
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()
複製代碼
提示: 由於RxSwift的核心邏輯大體相同,因此前面文章講過的核心邏輯這裏不重複,只講不一樣的地方,還不太明白的朋友能夠先看這兩篇文章RxSwift源碼分析(一)-核心邏輯解析和RxSwift源碼分析(二)-Observable和AnonymousObservableSink解析swift
言歸正傳,咱們一塊兒來看看RxSwift的timer是怎樣實現的。首先咱們須要探索RxSwift的timer在使用的過程當中有沒有如下幾個問題:api
runloop
的影響帶着問題,咱們進入RxSwift Timer的源碼分析bash
// 第一次參數:第一次響應距離如今的時間
// 第二個參數:時間間隔
// 第三個參數:線程
_ = Observable<Int>.timer(DispatchTimeInterval.seconds(0), period: DispatchTimeInterval.seconds(1), scheduler: MainScheduler.instance).subscribe { (event) in
print(event)
}
複製代碼
如上,RxSwift Timer使用起來很是簡單。 先進入到Timer.swift文件找到timer
方法, 這是一個ObservableType
協議的的擴展方法,返回的是一個Timer
對象,其類型是Observable
。因此Timer
也是一個可觀察序列,咱們能夠經過訂閱來接收觀察者發送的消息。多線程
public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval? = nil, scheduler: SchedulerType)
-> Observable<Element> {
return Timer(
dueTime: dueTime,
period: period,
scheduler: scheduler
)
}
複製代碼
咱們繼續跟蹤到Timer
類裏面去看一看Timer
究竟是怎麼實現的閉包
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 {
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對象,而後調用了subscribe
方法進行了訂閱。若是看過上兩篇關於RxSwift核心邏輯分析的文章的話,咱們知道當調用了subscribe
方法的以後,代碼會執行到Producer
類的subscribe
方法,而後執行到Timer
類的run
方法。在run
方法中建立TimerSink
對象並保持了Timer
對象,而後執行sink.run
,這個流程跟其餘序列的執行是同樣的。進入到TimerSink
類裏面:ide
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
}
}
}
複製代碼
_parent
就是Timer
對象,而後一直跟蹤schedulePeriodic
進入,最後會來到DispatchQueueConfiguration
類的schedulePeriodic
方法函數
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
let cancelTimer = Disposables.create {
timerReference?.cancel()
timerReference = nil
}
timer.setEventHandler(handler: {
if cancelTimer.isDisposed {
return
}
timerState = action(timerState)
})
timer.resume()
return cancelTimer
}
複製代碼
能夠看到,RxSwift的timer實際上是封裝的DispatchSource
定時器。當序列銷燬時會執行定時器的cancel
方法取消來定時器。當定時器觸發時會調用action(timerState)
這個代碼塊並保存代碼塊的返回值state(初始值爲0)。這個代碼塊就是以前調用TimerSink
類的run
方法時的尾隨閉包。這個函數會在咱們外面設置的那個主線程scheduler中調用,爲了防止多線程調用致使數據錯誤,這裏加了線程鎖。而後執行forwardOn
這個方法,後面的流程又跟以前兩篇文章講過的RxSwift核心邏輯同樣,這裏就不講了。state是遵循FixedWidthInteger
協議的變量,state &+ 1是位操做,每執行一次+1,&+運算符這種寫法能夠防止溢出,能夠參考文檔。oop
針對上面提出的問題總結一下:源碼分析
DispatchSource
定時器是不會受runloop
的影響的DispatchSource
並無直接引用self,因此不存在循環引用帶來的問題MainScheduler
實例,也就指定了代碼執行的線程。