開(kāi)發(fā)過(guò)程中韩容,遇到在某個(gè)時(shí)間或按照某個(gè)周期來(lái)執(zhí)行一些方法的時(shí)候齐婴,就會(huì)用到定時(shí)器单匣。下面就是幾種開(kāi)發(fā)中常見(jiàn)的定時(shí)器的創(chuàng)建方式蚁阳。
1.Timer(NSTimer)
(1) target action:
func testTimer(){
let timer = Timer.init(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
// 需要手動(dòng)加入到runLoop中
// 如果想不受scrollView頁(yè)面滑動(dòng)影響(滑動(dòng)時(shí)不響應(yīng)selector铃绒,滑動(dòng)停止后恢復(fù)),需設(shè)置當(dāng)前runloop的commonMode模式
RunLoop.current.add(timer, forMode: .default)
}
@objc func timerAction(){
print("timerAction")
}
(2) scheduledTimer block
// 默認(rèn)添加到runLoop中螺捐,使用defaultMode模式
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (timer) in
print("timerAction")
})
// 可以手動(dòng)設(shè)置當(dāng)前runloop的commonMode模式避免頁(yè)面滑動(dòng)影響
RunLoop.current.add(timer, forMode: .common)
NSTimer的執(zhí)行依賴runLoop颠悬,如果當(dāng)前runLoop正在執(zhí)行一個(gè)連續(xù)性的運(yùn)算,timer就會(huì)被延時(shí)觸發(fā)定血。當(dāng)延遲超過(guò)timer的一個(gè)重復(fù)周期赔癌,會(huì)在延時(shí)結(jié)束后按照指定的周期繼續(xù)執(zhí)行。
2.GCD(DispatchSourceTimer)
let gcdTimer = DispatchSource.makeTimerSource()
gcdTimer.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(1))
gcdTimer.setEventHandler(handler: {
print("GCD timerAction")
})
// 默認(rèn)掛起狀態(tài)澜沟,需手動(dòng)啟動(dòng)
gcdTimer.resume()
// 掛起灾票,掛起狀態(tài)時(shí)不能釋放(置為nil),會(huì)導(dǎo)致崩潰
// 可以再次resume()喚起定時(shí)器
gcdTimer.suspend()
// 取消茫虽,解釋定時(shí)任務(wù)
// 取消任務(wù)后如果想再次執(zhí)行Timer刊苍,只能重新創(chuàng)建一個(gè) Timer
gcdTimer.cancel()
gcdTimer = nil
GCD創(chuàng)建的Timer不受runLoop影響既们,使用DispatchSource,可以實(shí)現(xiàn)更加精準(zhǔn)的定時(shí)效果正什。
3.CADisplayLink
let cadTimer = CADisplayLink(target: self, selector: #selector(timerAction))
// 單位是幀啥纸,屏幕刷新多少幀時(shí)調(diào)用一次selector
// 默認(rèn)值為0,選擇使用設(shè)備的最高屏幕刷新頻率(iOS為60次每秒)
// 所以執(zhí)行時(shí)間間隔為:preferredFramesPerSecond * 最高屏幕刷新間隔婴氮,如iOS設(shè)備:preferredFramesPerSecond * 1/60 秒
cadTimer.preferredFramesPerSecond = 20
// 注冊(cè)到runLoop中監(jiān)聽(tīng)
cadTimer.add(to: RunLoop.current, forMode: .default)
// 掛起
// cadTimer.isPaused = true
// 終止
cadTimer?.invalidate()
cadTimer = nil
由于跟屏幕刷新同步脾拆,非常適合UI的重復(fù)繪制,如:下載進(jìn)度條莹妒,自定義動(dòng)畫設(shè)計(jì),視頻播放渲染等绰上。
4.RxSwift(interval/timer)
// interval方式
// period:每次發(fā)送的時(shí)間間隔旨怠,scheduler:調(diào)度者
timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
// timer方式
//timer = Observable<Int>.timer(1, scheduler: MainScheduler.instance)
timer?.subscribe(onNext: { (time) in
print(time)
})
.disposed(by: disposeBag)
源碼解析:
interval,timer創(chuàng)建都是使用以下初始化方式蜈块。
public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval? = nil, scheduler: SchedulerType)
-> Observable<Element> {
return Timer(
dueTime: dueTime,
period: period,
scheduler: scheduler
)
}
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)
}
}
}
了解過(guò)RxSwift的同學(xué)鉴腻,Timer
類是不是很眼熟,是不是和AnonymousObservable
非常相似百揭,一樣的繼承Producer
爽哎,一樣的init
方式,一樣的run
方法中創(chuàng)建sink
器一。有興趣的同學(xué)可以看看這兩篇RxSwift(二)原理-執(zhí)行流程
课锌,RxSwift(三)原理深入探究。
原理和RxSwift核心原理差不多祈秕,只是生成了TimerSink
或者TimerOneOffSink
渺贤,sink
調(diào)用run
方法,以TimerSink
為例:
跟一下源碼:sink.run()
final private class TimerSink<Observer: ObserverType> : Sink<Observer> where Observer.Element : RxAbstractInteger {
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
}
}
}
public class SerialDispatchQueueScheduler : SchedulerType {
/**
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: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
return self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)
}
}
extension DispatchQueueConfiguration {
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
}
}
可以看到這里其實(shí)就是初始化了一個(gè)DCD Timer请毛,這里有一句關(guān)鍵代碼:timerState = action(timerState)
志鞍。
這里的action
就是schedulePeriodic
傳入的閉包:
{ state in
self._lock.lock(); defer { self._lock.unlock() }
self.forwardOn(.next(state))
return state &+ 1
}
閉包里通過(guò)state
的遞增,重復(fù)調(diào)用self.forwardOn(.next(state))
方仿,這句代碼會(huì)調(diào)用self._observer.on(event)
固棚,將event傳入到subscribe()
訂閱方法中,訂閱方法創(chuàng)建匿名觀察者AnonymousObserver
保存的閉包會(huì)根據(jù)傳入的event
枚舉值對(duì)應(yīng)到onNext()
仙蚜。
RxSwift的timer是封裝的DispatchSource定時(shí)器的此洲,所以也是不受runloop影響的。