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