在軟件開發(fā)過程中握础,我們常常需要在某個時間后執(zhí)行某個方法辐董,或者是按照某個周期一直執(zhí)行某個方法。在這個時候禀综,我們就需要用到定時器简烘。
然而苔严,在iOS中有很多方法完成以上的任務(wù),到底有多少種方法呢孤澎?經(jīng)過查閱資料届氢,大概有三種方法:NSTimer、CADisplayLink覆旭、GCD退子。接下來我就一一介紹它們的用法。
一型将、NSTimer
1. 創(chuàng)建方法
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:NO];
TimerInterval : 執(zhí)行之前等待的時間寂祥。比如設(shè)置成1.0,就代表1秒后執(zhí)行方法
target : 需要執(zhí)行方法的對象茶敏。
selector : 需要執(zhí)行的方法
repeats : 是否需要循環(huán)
2. 釋放方法
[timer invalidate];
注意 :
調(diào)用創(chuàng)建方法后壤靶,target對象的計數(shù)器會加1,直到執(zhí)行完畢惊搏,自動減1贮乳。如果是循環(huán)執(zhí)行的話,就必須手動關(guān)閉恬惯,否則可以不執(zhí)行釋放方法向拆。
3. 特性
存在延遲
不管是一次性的還是周期性的timer的實際觸發(fā)事件的時間,都會與所加入的RunLoop和RunLoop Mode有關(guān)酪耳,如果此RunLoop正在執(zhí)行一個連續(xù)性的運算浓恳,timer就會被延時出發(fā)。重復(fù)性的timer遇到這種情況碗暗,如果延遲超過了一個周期颈将,則會在延時結(jié)束后立刻執(zhí)行,并按照之前指定的周期繼續(xù)執(zhí)行言疗。
必須加入Runloop
使用上面的創(chuàng)建方式晴圾,會自動把timer加入MainRunloop的NSDefaultRunLoopMode中。如果使用以下方式創(chuàng)建定時器噪奄,就必須手動加入Runloop:
NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
4. NSTimer會是準(zhǔn)時觸發(fā)事件嗎
答案是否定的死姚,而且有時候你會發(fā)現(xiàn)實際的觸發(fā)時間跟你想象的差距還比較大。NSTimer不是一個實時系統(tǒng)勤篮,因此不管是一次性的還是周期性的timer的實際觸發(fā)事件的時間可能都會跟我們預(yù)想的會有出入都毒。差距的大小跟當(dāng)前我們程序的執(zhí)行情況有關(guān)系,比如可能程序是多線程的碰缔,而你的timer只是添加在某一個線程的runloop的某一種指定的runloopmode中账劲,由于多線程通常都是分時執(zhí)行的,而且每次執(zhí)行的mode也可能隨著實際情況發(fā)生變化。
假設(shè)你添加了一個timer指定2秒后觸發(fā)某一個事件涤垫,但是簽好那個時候當(dāng)前線程在執(zhí)行一個連續(xù)運算(例如大數(shù)據(jù)塊的處理等)姑尺,這個時候timer就會延遲到該連續(xù)運算執(zhí)行完以后才會執(zhí)行竟终。重復(fù)性的timer遇到這種情況蝠猬,如果延遲超過了一個周期,則會和后面的觸發(fā)進(jìn)行合并统捶,即在一個周期內(nèi)只會觸發(fā)一次榆芦。但是不管該timer的觸發(fā)時間延遲的有多離譜,他后面的timer的觸發(fā)時間總是倍數(shù)于第一次添加timer的間隙喘鸟。
二匆绣、CADisplayLink
1. 創(chuàng)建方法
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
2. 停止方法
[self.displayLink invalidate];
self.displayLink = nil;
當(dāng)把CADisplayLink對象add到runloop中后,selector就能被周期性調(diào)用什黑,類似于重復(fù)的NSTimer被啟動了崎淳;執(zhí)行invalidate操作時,CADisplayLink對象就會從runloop中移除愕把,selector調(diào)用也隨即停止拣凹,類似于NSTimer的invalidate方法。
3. 特性
屏幕刷新時調(diào)用
CADisplayLink是一個能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫到屏幕上的定時器類恨豁。CADisplayLink以特定模式注冊到runloop后嚣镜,每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束的時候,runloop就會向CADisplayLink指定的target發(fā)送一次指定的selector消息橘蜜, CADisplayLink類對應(yīng)的selector就會被調(diào)用一次菊匿。所以通常情況下,按照iOS設(shè)備屏幕的刷新率60次/秒延遲
iOS設(shè)備的屏幕刷新頻率是固定的计福,CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用跌捆,精確度相當(dāng)高。但如果調(diào)用的方法比較耗時象颖,超過了屏幕刷新周期佩厚,就會導(dǎo)致跳過若干次回調(diào)調(diào)用機會。
如果CPU過于繁忙力麸,無法保證屏幕60次/秒的刷新率可款,就會導(dǎo)致跳過若干次調(diào)用回調(diào)方法的機會,跳過次數(shù)取決CPU的忙碌程度克蚂。使用場景
從原理上可以看出闺鲸,CADisplayLink適合做界面的不停重繪,比如視頻播放的時候需要不停地獲取下一幀用于界面渲染埃叭。
4. 重要屬性
frameInterval
NSInteger類型的值摸恍,用來設(shè)置間隔多少幀調(diào)用一次selector方法,默認(rèn)值是1,即每幀都調(diào)用一次立镶。duration
readOnly的CFTimeInterval值壁袄,表示兩次屏幕刷新之間的時間間隔。需要注意的是媚媒,該屬性在target的selector被首次調(diào)用以后才會被賦值嗜逻。selector的調(diào)用間隔時間計算方式是:調(diào)用間隔時間 = duration × frameInterval。
三: GCD的定時器 (最為準(zhǔn)確)
首先缭召,我們知道NStimer是在RunLoop的基礎(chǔ)上執(zhí)行的栈顷,然而RunLoop是在GCD基礎(chǔ)上實現(xiàn)的,所以說GCD可算是更加高級嵌巷。
- (void)gcdTimerConfig{
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"xsxsxsxs");
});
dispatch_resume(self.timer);
}