iOS中定時器有三種,分別是NSTimer、CADisplayLink、dispatch_source,下面就分別對這三種計時器進行說明
一、NSTimer
- 創建方法
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:NO];
TimerInterval
: 執行之前等待的時間。比如設置成1.0,就代表1秒后執行方法
target
: 需要執行方法的對象。
selector
: 需要執行的方法
repeats
: 是否需要循環
- 釋放方法
[timer invalidate];
timer = nil;
注意 :
調用創建方法后,target
對象的計數器會加1,直到執行完畢,自動減1。如果是循環執行的話,就必須手動關閉,否則可以不執行釋放方法。
- 特性
存在延遲
不管是一次性的還是周期性的timer的實際觸發事件的時間,都會與所加入的RunLoop
和RunLoop Mode
有關,如果此RunLoop
正在執行一個連續性的運算,timer
就會被延時出發。重復性的timer
遇到這種情況,如果延遲超過了一個周期,則會在延時結束后立刻執行,并按照之前指定的周期繼續執行。必須加入Runloop
使用上面的創建方式,會自動把timer
加入MainRunloop
的NSDefaultRunLoopMode
中。如果使用以下方式創建定時器,就必須手動加入Runloop
:
NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
二、CADisplayLink
- 創建方法
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
- 停止方法
[self.displayLink invalidate];
self.displayLink = nil;
當把CADisplayLink
對象add
到runloop
中后,selector
就能被周期性調用,類似于重復的NSTimer
被啟動了;執行invalidate
操作時,CADisplayLink
對象就會從runloop
中移除,selector
調用也隨即停止,類似于NSTimer
的invalidate
方法。
- 特性
-
屏幕刷新時調用
CADisplayLink
是一個能讓我們以和屏幕刷新率同步的頻率將特定的內容畫到屏幕上的定時器類。CADisplayLink
以特定模式注冊到runloop
后,每當屏幕顯示內容刷新結束的時候,runloop
就會向CADisplayLink
指定的target
發送一次指定的selector
消息,CADisplayLink
類對應的selector
就會被調用一次。所以通常情況下,按照iOS設備屏幕的刷新率60次/秒
- 延遲
iOS
設備的屏幕刷新頻率是固定的,CADisplayLink
在正常情況下會在每次刷新結束都被調用,精確度相當高。但如果調用的方法比較耗時,超過了屏幕刷新周期,就會導致跳過若干次回調調用機會。
如果CPU過于繁忙,無法保證屏幕60次/秒
的刷新率,就會導致跳過若干次調用回調方法的機會,跳過次數取決CPU
的忙碌程度。
- 延遲
使用場景
從原理上可以看出,CADisplayLink
適合做界面的不停重繪,比如視頻播放的時候需要不停地獲取下一幀用于界面渲染。重要屬性
frameInterval
NSInteger
類型的值,用來設置間隔多少幀調用一次selector
方法,默認值是1,即每幀都調用一次。duration
readOnly
的CFTimeInterval
值,表示兩次屏幕刷新之間的時間間隔。需要注意的是,該屬性在target
的selector
被首次調用以后才會被賦值。selector
的調用間隔時間計算方式是:調用間隔時間 = duration × frameInterval
。
三、dispatch_source
- 創建方法
//需要將dispatch_source_t timer設置為成員變量,不然會立即釋放
@property (nonatomic, strong) dispatch_source_t timer;
//定時器開始執行的延時時間
NSTimeInterval delayTime = 3.0f;
//定時器間隔時間
NSTimeInterval timeInterval = 3.0f;
//創建子線程隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//使用之前創建的隊列來創建計時器
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//設置延時執行時間,delayTime為要延時的秒數
dispatch_time_t startDelayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC));
//設置計時器
dispatch_source_set_timer(_timer, startDelayTime, timeInterval * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^{
//執行事件
});
// 啟動計時器
dispatch_resume(_timer);
- 停止方法
dispatch_source_cancel(_timer);
- 特性
- 默認是重復執行的,可以在事件響應回調中通過
dispatch_source_cancel
方法來設置為只執行一次,如下代碼:
dispatch_source_set_event_handler(_timer, ^{
//執行事件
dispatch_source_cancel(_timer);
});```
4. 重要屬性
dispatch_source_set_timer(dispatch_source_t source,
dispatch_time_t start,
uint64_t interval,
uint64_t leeway);
* start
計時器起始時間,可以通過`dispatch_time`創建,如果使用`DISPATCH_TIME_NOW`,則創建后立即執行
* interval
計時器間隔時間,可以通過`timeInterval * NSEC_PER_SEC`來設置,其中,
`timeInterval`為對應的秒數
* leeway
這個參數的理解,我覺得[http://www.dreamingwish.com](http://www.dreamingwish.com)上Seven's
同學的解釋很直觀也很易懂:“這個參數告訴系統我們需要計時器觸發的精準程度。所有的計時器都不會保證100%精準,這個參數用來告訴系統你希望系統保證精準的努力程度。如果你希望一個計時器沒五秒觸發一次,并且越準越好,那么你傳遞0為參數。另外,如果是一個周期性任務,比如檢查email,那么你會希望每十分鐘檢查一次,但是不用那么精準。所以你可以傳入60,告訴系統60秒的誤差是可接受的。這樣有什么意義呢?簡單來說,就是降低資源消耗。如果系統可以讓cpu休息足夠長的時間,并在每次醒來的時候執行一個任務集合,而不是不斷的醒來睡去以執行任務,那么系統會更高效。如果傳入一個比較大的leeway給你的計時器,意味著你允許系統拖延你的計時器來將計時器任務與其他任務聯合起來一起執行。
5. 優點:
* 時間準確
* 可以使用子線程,解決定時間跑在主線程上卡UI問題
6. 注意事項:
需要將dispatch_source_t timer設置為成員變量,不然會立即釋放
#####參考:
[控制了時間,就控制了一切!-iOS中幾種定時器](http://www.lxweimin.com/p/21d351116587)