首先要注意 : tableview,scrollview,滑動時NSTimer的加入到run Loop中 要不然的話滑動阻礙NSTimer 運轉 界面是主線程
在軟件開發過程中,我們常常需要在某個時間后執行某個方法,或者是按照某個周期一直執行某個方法。在這個時候,我們就需要用到定時器。
然而,在iOS中有很多方法完成以上的任務,到底有多少種方法呢?經過查閱資料,大概有三種方法:NSTimer、CADisplayLink、GCD。接下來我就一一介紹它們的用法。
一、NSTimer
1. 創建方法(會自動把timer加入MainRunloop的NSDefaultRunLoopMode中)
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:NO];
-
TimerInterval : 執行之前等待的時間。比如設置成1.0,就代表1秒后執行方法
-
target : 需要執行方法的對象。
-
selector : 需要執行的方法
-
repeats : 是否需要循環
2. 釋放方法
[timer invalidate];
-
注意 :
調用創建方法后,target對象的計數器會加1,直到執行完畢,自動減1。如果是循環執行的話,就必須手動關閉,否則可以不執行釋放方法。
3. 特性
-
存在延遲
不管是一次性的還是周期性的timer的實際觸發事件的時間,都會與所加入的RunLoop和RunLoop Mode有關,如果此RunLoop正在執行一個連續性的運算,timer就會被延時出發。重復性的timer遇到這種情況,如果延遲超過了一個周期,則會在延時結束后立刻執行,並按照之前指定的周期繼續執行。
4.注意點:
主線程的 RunLoop 里有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。這兩個 Mode 都已經被標記為"Common"屬性。DefaultMode 是 App 平時所處的狀態,TrackingRunLoopMode 是追蹤 ScrollView 滑動時的狀態。當你創建一個 Timer 並加到 DefaultMode 時,Timer 會得到重復回調,但此時滑動一個TableView時,RunLoop 會將 mode 切換為 TrackingRunLoopMode,這時 Timer 就不會被回調,不會影響到滑動操作(滑動時定時器不工作了)
NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
//[NSRunLoop mainRunLoop] 這個適用於主線程中獲取runloop
//[NSRunLoop currentRunLoop]這個主線程/子線程 都適用 獲取runloop
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];// Mode:NSRunLoopDefaultMode
滑動和定時器調用互不影響:
有時你需要一個 Timer在兩個 Mode 中都能得到回調。一種辦法就是將這個 Timer 分別加入這兩個 Mode;還有一種方式就是將 Timer 加入到頂層的 RunLoop 的 "commonModeItems" 中,"commonModeItems" 會被 RunLoop 自動更新到所有具有"Common"屬性的 Mode 里去
NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
//[NSRunLoop mainRunLoop] 這個適用於主線程中獲取runloop
//[NSRunLoop currentRunLoop]這個主線程/子線程 都適用 獲取runloop
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];//NSRunLoopCommonModes//將 Timer 加入到頂層的 RunLoop 的 "commonModeItems"中,"commonModeItems" 會被 RunLoop 自動更新到所有具有"Common"屬性的 Mode 里去
二、CADisplayLink
1. 創建方法
```objc self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)]; [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; ```
2. 停止方法
```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方式
-
執行一次
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, ^{ //在這里執行事件 }); dispatch_resume(_timer);
關於定時器的注意點:
定時器的調用,放在主線程中最優! 在gcd dispatch_async中執行可能會無效!
調用一次計時器方法:
timer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(scrollTimer) userInfo:nil repeats:NO]; //不重復,只調用一次。timer運行一次就會自動停止運行
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(function:) userInfo:nil repeats:YES]; //每1秒運行一次function方法。
//取消定時器 [timer invalidate]; timer = nil;
2.先停止,然后再某種情況下再次開啟運行timer
//關閉定時器 [myTimer setFireDate:[NSDate distantFuture]];
//開啟定時器 [myTimer setFireDate:[NSDate distantPast]];
例子:比如,在頁面消失的時候關閉定時器,然后等頁面再次打開的時候,又開啟定時器
(主要是為了防止它在后台運行,暫用CPU)可以使用下面的代碼實現
//頁面將要進入前台,開啟定時器 -(void)viewWillAppear:(BOOL)animated { //開啟定時器 [scrollView.myTimer setFireDate:[NSDate distantPast]]; } //頁面消失,進入后台不顯示該頁面,關閉定時器 -(void)viewDidDisappear:(BOOL)animated { //關閉定時器 [scrollView.myTimer setFireDate:[NSDate distantFuture]]; }