RxSwift是一個比較大的框架,如果我們項目使用了這個框架的話,可以儘量去使用裏面封裝的一些功能,比如定時器Timer
。
首先說明源碼中提供的定時器方法有2個:
// 開啓一個定時器:在dueTime後,每隔period在scheduler中發送一次onNext:
// 這裏的period如果爲空,則是一個一次性的timer,觸發一次之後就結束
public static func timer(_ dueTime: RxSwift.RxTimeInterval, period: RxSwift.RxTimeInterval? = nil, scheduler: RxSwift.SchedulerType) -> RxSwift.Observable<Self.Element>
// 本方法是上面方法中dueTime = period的情況
public static func interval(_ period: RxSwift.RxTimeInterval, scheduler: RxSwift.SchedulerType) -> RxSwift.Observable<Self.Element>
因爲上述兩個方法的區別較小,所以這裏以interval
方法爲例:
一、使用方法
開啓定時器一般分爲在主線程、子線程兩種情況:
RxSwift中的Scheduler
var timerDispose: Disposable?
/// 1. 在主線程開啓定時器,並且在主線程回調onNext:
/// 定時器銷燬的時機在:信號執行dispose之後(disposeBag銷燬、onError、onComplete)
timerDispose = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
.subscribe(onNext: { num in
print(Thread.current, "subscribe===>", num)
})
timerDispose.disposed(by: self.disposeBag) // 在自身銷燬時銷燬定時器
// timerDispose.dispose() // 手動銷燬定時器的方法
/// 2. 在子線程開啓定時器,並且在子線程回調onNext:
timerDispose = Observable<Int>.interval(.seconds(2), scheduler: SerialDispatchQueueScheduler(internalSerialQueueName: "testTimer"))
.subscribe(onNext: { num in // num會調用一次加1
print(Thread.current, "===>", num)
DispatchQueue.main.async {
print("testTimer:回到主線程做事", Thread.current)
}
})
timerDispose.disposed(by: self.disposeBag) // 在自身銷燬時銷燬定時器
// timerDispose.dispose() // 手動銷燬定時器的方法
二、RxSwift中定時器的大體過程
- 創建RxSwift中的Timer對象並返回
public static func interval(_ period: RxTimeInterval, scheduler: SchedulerType)
-> Observable<Element> {
return Timer(dueTime: period, period: period, scheduler: scheduler )
}
--》2. 在Timer
被訂閱(subscribe
)時,首先會創建一個AnonymousObserver
類型的匿名observer
, 返回一個Disposable
, 裏面的self.asObservable().subscribe(observer),
中self是Timer實例,繼承自Producer
類,所以會執行Producer
類的subscribe
方法。
public func subscribe( onNext: ((Element) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil
) -> Disposable {
let disposable: disposable = Disposables.create()
let observer = AnonymousObserver<Element> { event in
switch event {
case .next(let value):
onNext?(value)
case .error(let error):// 省略
case .completed:// 省略
}
}
return Disposables.create(
self.asObservable().subscribe(observer), // 這裏的self是Timer
disposable
)
}
--》3. 接下來會執行Timer的父類Producer
中subscribe
方法,然後執行Timer
的run
方法。如下代碼中CurrentThreadScheduler.instance.schedule
函數內部主要邏輯是調用這個尾隨閉包,閉包中會調用self.run(observer, cancel: disposer)
, 這裏的self還是Timer。
/// Producer中subscribe
override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
return CurrentThreadScheduler.instance.schedule(()) { _ in
let disposer = SinkDisposer()
let sinkAndSubscription = self.run(observer, cancel: disposer)
disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
return disposer
}
}
/// 這裏是Timer的run方法
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 {// 一次性的定時器,這裏略 }
}
--》 4. 接下來是TimerSink
的實例調用sink.run()
, 可以看到內部是執行scheduler的schedulePeriodic
方法,方法最後一個參數action
是尾隨閉包,這裏的scheduler
是我們創建定時器時創建的,因爲MainScheduler
是繼承SerialDispatchQueueScheduler
的,所以這裏以後者來追蹤。可以看到會轉給scheduler內部的configuration執行schedulePeriodic
方法
func run() -> Disposable {
return self.parent.scheduler.schedulePeriodic(0 as Observer.Element, startAfter: self.parent.dueTime, period: self.parent.period!) { state in
self.lock.performLocked {
self.forwardOn(.next(state))
return state &+ 1 // 調用一次之後讓state+1,這樣在訂閱next裏面的num會每次加1
}
}
}
/// SerialDispatchQueueScheduler中的schedulePeriodic方法,會轉給
public func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)
}
--》5. scheduler內部的configuration執行schedulePeriodic
方法的邏輯, 可以看到內部是在self.queue
中創建了一個TimerSource
, 定時器每次觸發時,會執行timerState = action(timerState)
即第4步中傳進來的action, 可以看到是會執行self.forwardOn(.next(state))
, 即發送一個next事件 --> 最終使用者收到onNext
調用.
func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
let initial = DispatchTime.now() + startAfter
var timerState = state
// self.queue是在scheduler創建時內部創建的,所以是不是主線程取決於創建scheduler的類型
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
}
--》 6. 我們在使用時如果對時間準確度要求比較高的可以使用MainScheduler.instance
; 如果對時間準確度要求高的可以使用SerialDispatchQueueScheduler(internalSerialQueueName: "testTimer")
三、 銷燬定時器
銷燬定時器的方法:
使用定時器時引用返回的Disposable,在不需要定時器時執行dispose即可
原理:
可以看到在上述第2步中,創建匿名observer時會返回Disposable, 內部是引用了匿名observer的,在Disposable執行dispose時,observer也會失去引用而銷燬; 然後在上述的第5步中可以看到cancelTimer
在執行dispose
時,會執行timerReference?.cancel() ; timerReference = nil
。
四、 使用系統定時器
系統定時器:
1、系統封裝的Timer
,使用起來跟NSTimer是一樣的。
2、系統的GCDTiemr,使用方法跟第5步是一樣的, 缺少了當前是第幾次觸發的功能。
我更傾向於使用RxSwift中定時器是因爲以下幾個原因:
- 使用也很簡單方便
- 能夠知道當前是第幾次定時觸發