一直以來都想好好學(xué)習(xí)下CoreAnimation,奈何涉及的東西太多,想要一次性全部搞定時間上不允許,以后會斷斷續(xù)續(xù)的補全。最近項目里用到了CADisplayLink,就順便花點時間看了看。
一、簡介
1、所在框架
CADisplayLink和其它CoreAnimation類一樣,都是在QuartzCore.framework里。
2、功能
CADisplayLink最主要的特征是能提供一個周期性的調(diào)用我們賦給它的selector的機制,從這點上看它很像定時器NSTimer。
3、使用方式
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
//do something
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
當(dāng)把CADisplayLink對象add到runloop中后,selector就能被周期性調(diào)用,類似于NSTimer被啟動了;執(zhí)行invalidate操作時, CADisplayLink對象就會從runloop中移除,selector 調(diào)用也隨即停止,類似于NSTimer的invalidate方法。
二、特性
下面結(jié)合NSTimer來介紹 CADisplayLink,與NSTimer不同的地方有:
1、原理不同
CADisplayLink是一個能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫到屏幕上的定時器類。 CADisplayLink以特定模式注冊到runloop后, 每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束的時候,runloop就會向 CADisplayLink指定的target發(fā)送一次指定的selector消息,??CADisplayLink類對應(yīng)的selector就會被調(diào)用一次。
NSTimer以指定的模式注冊到runloop后,每當(dāng)設(shè)定的周期時間到達后,runloop會向指定的target發(fā)送一次指定的selector消息。
2、周期設(shè)置方式不同
iOS設(shè)備的屏幕刷新頻率(FPS)是60Hz,因此CADisplayLink的selector 默認調(diào)用周期是每秒60次,這個周期可以通過frameInterval屬性設(shè)置, CADisplayLink的selector每秒調(diào)用次數(shù)=60/ frameInterval。比如當(dāng) frameInterval設(shè)為2,每秒調(diào)用就變成30次。因此, CADisplayLink 周期的設(shè)置方式略顯不便。
NSTimer的selector調(diào)用周期可以在初始化時直接設(shè)定,相對就靈活的多。
3、精確度不同
iOS設(shè)備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用,精確度相當(dāng)高。
NSTimer的精確度就顯得低了點,比如NSTimer的觸發(fā)時間到的時候,runloop如果在忙于別的調(diào)用,觸發(fā)時間就會推遲到下一 個runloop周期。更有甚者,在OS X v10.9以后為了盡量避免在NSTimer觸發(fā)時間到了而去中斷當(dāng)前處理的任務(wù),NSTimer新增了tolerance屬性,讓用戶可以設(shè)置可以容忍 的觸發(fā)的時間范圍。
4、使用場合
從原理上不難看出, CADisplayLink 使用場合相對專一, 適合做界面的不停重繪,比如視頻播放的時候需要不停地獲取下一幀用于界面渲染。
NSTimer的使用范圍要廣泛的多,各種需要單次或者循環(huán)定時處理的任務(wù)都可以使用。
三、重要屬性
下面不完整的列出了 CADisplayLink的幾個重要屬性:
1、 frameInterval
可讀可寫的NSInteger型值,標(biāo)識間隔多少幀調(diào)用一次selector方法,默認值是1,即每幀都調(diào)用一次。官方文檔中強調(diào),當(dāng)該值被設(shè)定小于1時,結(jié)果是不可預(yù)知的。
2、duration
只讀的CFTimeInterval值,表示兩次屏幕刷新之間的時間間隔。需要注意的是,該屬性在target的selector被首次調(diào)用以后才會被賦 值。selector的調(diào)用間隔時間計算方式是:時間=duration×frameInterval。
現(xiàn)存的iOS設(shè)備屏幕的FPS都是60Hz,這一點可以從CADisplayLink的duration屬性看出來。duration的值都是0.166666…,即1/60。盡管如此,我們并沒法確定蘋果不會改變 FPS ,如果以后某一天將 FPS 提升到了120Hz了怎么辦呢?這時,你設(shè)置了frameInterval屬性值為2期望每秒刷新30次,卻發(fā)現(xiàn)每秒刷新了60次,結(jié)果可想而知,出于安全考慮,還是先根據(jù)duration判斷屏幕的 FPS再去使用 CADisplayLink 。
3、timestamp
只讀的CFTimeInterval值,表示屏幕顯示的上一幀的時間戳,這個屬性通常被target用來計算下一幀中應(yīng)該顯示的內(nèi)容。
打印timestamp值,其樣式類似于:
179699.631584
雖然名為時間戳,但這和常見的unix時間戳差異很大,事實上這是CoreAnimation使用的時間格式。每個CALayer都有一個本地時間 (CALayer本地時間的具體作用會在后續(xù)文章中說明),可以獲取當(dāng)前CALayer的本地時間并打?。?/p>
CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];
NSLog("localLayerTime:%f",localLayerTime);
四、注意
iOS并不能保證能以每秒60次的頻率調(diào)用回調(diào)方法,這取決于:
1、CPU的空閑程度
如果CPU忙于其它計算,就沒法保證以60HZ執(zhí)行屏幕的繪制動作,導(dǎo)致跳過若干次調(diào)用回調(diào)方法的機會,跳過次數(shù)取決CPU的忙碌程度。
2、執(zhí)行回調(diào)方法所用的時間
如果執(zhí)行回調(diào)時間大于重繪每幀的間隔時間,就會導(dǎo)致跳過若干次回調(diào)調(diào)用機會,這取決于執(zhí)行時間長短。
五、參考文檔
1、官方文檔
2、官方使用CADisplayLink播放視頻的例子