此處列舉三種方法:NSTimer、CADisplayLink、GCD。
一、NSTimer
(1)創建
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:NO]; //TimerInterval執行之前等待的時間,1.0表示執行的時間間隔
(2)手動開始
- (void)fire;
(3)停止:調用創建方法后,target對象的計數器會加1,直到執行完畢,自動減1。如果是循環執行的話,就必須手動關閉,否則可以不執行釋放方法。
- (void)invalidate;
(4)存在延遲
不管是一次性的還是周期性的timer的實際觸發事件的時間,都會與所加入的RunLoop和RunLoop Mode有關,如果此RunLoop正在執行一個連續性的運算,timer就會被延時出發。重復性的timer遇到這種情況,如果延遲超過了一個周期,則會在延時結束后立刻執行,並按照之前指定的周期繼續執行。
運行在主線程上 當使用NSTimer的scheduledTimerWithTimeInterval方法時。事實上此時Timer會被加入到當前線程的Run Loop中,且模式是默認的NSDefaultRunLoopMode。而如果當前線程就是主線程,也就是UI線程時,某些UI事件,比如UIScrollView的拖動操作,會將Run Loop切換成NSEventTrackingRunLoopMode模式,在這個過程中,默認的NSDefaultRunLoopMode模式中注冊的事件是不會被執行的。也就是說,此時使用scheduledTimerWithTimeInterval添加到Run Loop中的Timer就不會執行。
為了設置一個不被UI干擾的Timer,我們需要手動創建一個Timer,然后使用NSRunLoop的addTimer:forMode:方法來把Timer按照指定模式加入到Run Loop中。這里使用的模式是:NSRunLoopCommonModes,這個模式等效於NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的結合,官方參考文檔
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
二、CADisplayLink
(1)使用
//創建 + (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel; - (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode; - (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode; //停止 - (void)invalidate; //設置定時器 CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(action)]; //開啟定時器 [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; -(void)stopLink { // 關閉定時器 [self.link invalidate]; self.link = nil; } (2)特性 CADisplayLink是一個能讓我們以和屏幕刷新率同步的頻率將特定的內容畫到屏幕上的定時器類。CADisplayLink以特定模式注冊到runloop后,每當屏幕顯示內容刷新結束的時候,runloop就會向CADisplayLink指定的target發送一次指定的selector消息, CADisplayLink類對應的selector就會被調用一次。所以通常情況下,按照iOS設備屏幕的刷新率60次/秒 iOS設備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會在每次刷新結束都被調用,精確度相當高。但如果調用的方法比較耗時,超過了屏幕刷新周期,就會導致跳過若干次回調調用機會。 如果CPU過於繁忙,無法保證屏幕60次/秒的刷新率,就會導致跳過若干次調用回調方法的機會,跳過次數取決CPU的忙碌程度。 (3)使用場景 從原理上可以看出,CADisplayLink適合做界面的不停重繪,比如視頻播放的時候需要不停地獲取下一幀用於界面渲染。 三、GCD方式 執行一次 double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ //執行事件}); 重復執行 NSTimeInterval period = 1.0; //設置時間間隔 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒執行 dispatch_source_set_event_handler(timer, ^{ NSLog(@"wakeup"); dispatch_source_cancel(timer); NSLog(@"end:%@",[formatter stringFromDate:[NSDate date]]); }); dispatch_source_set_cancel_handler(timer, ^{ NSLog(@"cancel"); NSLog(@"end:%@",[formatter stringFromDate:[NSDate date]]); }); //開啟定時器 dispatch_resume(_timer);
五、CADisplayLink
與 NSTimer
有什么不同
iOS設備的屏幕刷新頻率是固定的,CADisplayLink
在正常情況下會在每次刷新結束都被調用,精確度相當高。NSTimer
的精確度就顯得低了點,比如NSTimer
的觸發時間到的時候,runloop
如果在阻塞狀態,觸發時間就會推遲到下一個runloop
周期。並且 NSTimer
新增了tolerance
屬性,讓用戶可以設置可以容忍的觸發的時間的延遲范圍。 CADisplayLink
使用場合相對專一,適合做UI的不停重繪,比如自定義動畫引擎或者視頻播放的渲染。NSTimer
的使用范圍要廣泛的多,各種需要單次或者循環定時處理的任務都可以使用。在UI相關的動畫或者顯示內容使用 CADisplayLink
比起用NSTimer
的好處就是我們不需要在格外關心屏幕的刷新頻率了,因為它本身就是跟屏幕刷新同步的。