一昨登、什么是CADisplayLink
簡(jiǎn)單地說,它就是一個(gè)定時(shí)器贯底,每隔幾毫秒刷新一次屏幕丰辣。
CADisplayLink是一個(gè)能讓我們以和屏幕刷新率相同的頻率將內(nèi)容畫到屏幕上的定時(shí)器。我們?cè)趹?yīng)用中創(chuàng)建一個(gè)新的 CADisplayLink 對(duì)象丈甸,把它添加到一個(gè)runloop中糯俗,并給它提供一個(gè) target 和 selector 在屏幕刷新的時(shí)候調(diào)用尿褪。
一但 CADisplayLink 以特定的模式注冊(cè)到runloop之后睦擂,每當(dāng)屏幕需要刷新的時(shí)候,runloop就會(huì)調(diào)用CADisplayLink綁定的target上的selector杖玲,這時(shí)target可以讀到 CADisplayLink 的每次調(diào)用的時(shí)間戳顿仇,用來準(zhǔn)備下一幀顯示需要的數(shù)據(jù)。例如一個(gè)視頻應(yīng)用使用時(shí)間戳來計(jì)算下一幀要顯示的視頻數(shù)據(jù)摆马。在UI做動(dòng)畫的過程中臼闻,需要通過時(shí)間戳來計(jì)算UI對(duì)象在動(dòng)畫的下一幀要更新的大小等等。
在添加進(jìn)runloop的時(shí)候我們應(yīng)該選用高一些的優(yōu)先級(jí)囤采,來保證動(dòng)畫的平滑述呐。可以設(shè)想一下蕉毯,我們?cè)趧?dòng)畫的過程中乓搬,runloop被添加進(jìn)來了一個(gè)高優(yōu)先級(jí)的任務(wù),那么代虾,下一次的調(diào)用就會(huì)被暫停轉(zhuǎn)而先去執(zhí)行高優(yōu)先級(jí)的任務(wù)进肯,然后在接著執(zhí)行CADisplayLink的調(diào)用,從而造成動(dòng)畫過程的卡頓棉磨,使動(dòng)畫不流暢江掩。
CADisplayLink的屬性介紹:
1.duration屬性:提供了每幀之間的時(shí)間,也就是屏幕每次刷新之間的的時(shí)間。該屬性在target的selector被首次調(diào)用以后才會(huì)被賦值环形。selector的調(diào)用間隔時(shí)間計(jì)算方式是:時(shí)間=duration×frameInterval策泣。 我們可以使用這個(gè)時(shí)間來計(jì)算出下一幀要顯示的UI的數(shù)值。但是 duration只是個(gè)大概的時(shí)間斟赚,如果CPU忙于其它計(jì)算着降,就沒法保證以相同的頻率執(zhí)行屏幕的繪制操作,這樣會(huì)跳過幾次調(diào)用回調(diào)方法的機(jī)會(huì)拗军。
2.frameInterval屬性:是可讀可寫的NSInteger型值任洞,標(biāo)識(shí)間隔多少幀調(diào)用一次selector 方法,默認(rèn)值是1发侵,即每幀都調(diào)用一次交掏。如果每幀都調(diào)用一次的話,對(duì)于iOS設(shè)備來說那刷新頻率就是60HZ也就是每秒60次刃鳄,如果將 frameInterval 設(shè)為2 那么就會(huì)兩幀調(diào)用一次盅弛,也就是變成了每秒刷新30次。
3.pause屬性:控制CADisplayLink的運(yùn)行叔锐。當(dāng)我們想結(jié)束一個(gè)CADisplayLink的時(shí)候挪鹏,應(yīng)該調(diào)用-(void)invalidate 從runloop中刪除并刪除之前綁定的 target 跟 selector
4.timestamp屬性: 只讀的CFTimeInterval值,表示屏幕顯示的上一幀的時(shí)間戳愉烙,這個(gè)屬性通常被target用來計(jì)算下一幀中應(yīng)該顯示的內(nèi)容讨盒。 打印timestamp值,其樣式類似于:179699.631584步责。
另外 CADisplayLink 不能被繼承返顺。
給非UI對(duì)象添加動(dòng)畫效果
我們知道動(dòng)畫效果就是一個(gè)屬性的線性變化,比如 UIView 動(dòng)畫的 EasyIn EasyOut 蔓肯。通過數(shù)值按照不同速率的變化我們能生成更接近真實(shí)世界的動(dòng)畫效果遂鹊。我們也可以利用這個(gè)特性來使一些其他屬性按照我們期望的曲線變化。比如當(dāng)播放視頻時(shí)關(guān)掉視頻的聲音我可以通過 CADisplayLink 來實(shí)現(xiàn)一個(gè) EasyOut 的漸出效果:先快速的降低音量蔗包,在慢慢的漸變到靜音秉扑。
注意:通常來講:iOS設(shè)備的刷新頻率事60HZ也就是每秒60次。那么每一次刷新的時(shí)間就是1/60秒 大概16.7毫秒调限。當(dāng)我們的frameInterval 值為1的時(shí)候我們需要保證的是 CADisplayLink調(diào)用的target的函數(shù)計(jì)算時(shí)間不應(yīng)該大于 16.7否則就會(huì)出現(xiàn)嚴(yán)重的丟幀現(xiàn)象舟陆。 在mac應(yīng)用中我們使用的不是CADisplayLink而是 CVDisplayLink它是基于C接口的用起來配置有些麻煩但是用起來還是很簡(jiǎn)單的。
二旧噪、CADisplayLink 與 NSTimer 有什么不同?
1.原理不同
CADisplayLink是一個(gè)能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫到屏幕上的定時(shí)器類吨娜。 CADisplayLink以特定模式注冊(cè)到runloop后, 每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束的時(shí)候淘钟,runloop就會(huì)向 CADisplayLink指定的target發(fā)送一次指定的selector消息宦赠, CADisplayLink類對(duì)應(yīng)的selector就會(huì)被調(diào)用一次。
NSTimer以指定的模式注冊(cè)到runloop后,每當(dāng)設(shè)定的周期時(shí)間到達(dá)后勾扭,runloop會(huì)向指定的target發(fā)送一次指定的selector消息毡琉。
2.周期設(shè)置方式不同
iOS設(shè)備的屏幕刷新頻率(FPS)是60Hz,因此CADisplayLink的selector 默認(rèn)調(diào)用周期是每秒60次妙色,這個(gè)周期可以通過frameInterval屬性設(shè)置桅滋, CADisplayLink的selector每秒調(diào)用次數(shù)=60/ frameInterval。比如當(dāng) frameInterval設(shè)為2身辨,每秒調(diào)用就變成30次丐谋。因此, CADisplayLink 周期的設(shè)置方式略顯不便煌珊。
NSTimer的selector調(diào)用周期可以在初始化時(shí)直接設(shè)定号俐,相對(duì)就靈活的多。
3定庵、精確度不同
iOS設(shè)備的屏幕刷新頻率是固定的吏饿,CADisplayLink在正常情況下會(huì)在每次刷新結(jié)束都被調(diào)用,精確度相當(dāng)高蔬浙。
NSTimer的精確度就顯得低了點(diǎn)猪落,比如NSTimer的觸發(fā)時(shí)間到的時(shí)候,runloop如果在阻塞狀態(tài)畴博,觸發(fā)時(shí)間就會(huì)推遲到下一個(gè)runloop周期笨忌。并且 NSTimer新增了tolerance屬性,讓用戶可以設(shè)置可以容忍的觸發(fā)的時(shí)間的延遲范圍绎晃。
4蜜唾、使用場(chǎng)景
CADisplayLink使用場(chǎng)合相對(duì)專一杂曲,適合做UI的不停重繪庶艾,比如自定義動(dòng)畫引擎或者視頻播放的渲染。
NSTimer的使用范圍要廣泛的多擎勘,各種需要單次或者循環(huán)定時(shí)處理的任務(wù)都可以使用咱揍。
三、CADisplayLink和NSTimer的使用方法
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對(duì)象add到runloop中后棚饵,selector就能被周期性調(diào)用煤裙,類似于重復(fù)的NSTimer被啟動(dòng)了;執(zhí)行invalidate操作時(shí)噪漾,CADisplayLink對(duì)象就會(huì)從runloop中移除硼砰,selector調(diào)用也隨即停止,類似于NSTimer的invalidate方法欣硼。-
NSTimer的使用
- 創(chuàng)建方法
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:NO];
TimerInterval : 執(zhí)行之前等待的時(shí)間题翰。比如設(shè)置成1.0,就代表1秒后執(zhí)行方法
target : 需要執(zhí)行方法的對(duì)象。
selector : 需要執(zhí)行的方法
repeats : 是否需要循環(huán)
- 創(chuàng)建方法
釋放方法
[timer invalidate];
注意 :調(diào)用創(chuàng)建方法后豹障,target對(duì)象的計(jì)數(shù)器會(huì)加1冯事,直到執(zhí)行完畢,自動(dòng)減1血公。如果是循環(huán)執(zhí)行的話昵仅,就必須手動(dòng)關(guān)閉,否則可以不執(zhí)行釋放方法累魔。特性
存在延遲 摔笤,不管是一次性的還是周期性的timer的實(shí)際觸發(fā)事件的時(shí)間,都會(huì)與所加入的RunLoop和RunLoop Mode有關(guān)垦写,如果此RunLoop正在執(zhí)行一個(gè)連續(xù)性的運(yùn)算籍茧,timer就會(huì)被延時(shí)出發(fā)。重復(fù)性的timer遇到這種情況梯澜,如果延遲超過了一個(gè)周期寞冯,則會(huì)在延時(shí)結(jié)束后立刻執(zhí)行,并按照之前指定的周期繼續(xù)執(zhí)行晚伙。
注意:必須加入Runloop
使用上面的創(chuàng)建方式吮龄,會(huì)自動(dòng)把timer加入MainRunloop的NSDefaultRunLoopMode中。如果使用以下方式創(chuàng)建定時(shí)器咆疗,就必須手動(dòng)加入Runloop:
NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];