日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS之深入探究CADisplayLink和NSTimer的对比和内存溢出问题

發布時間:2024/5/21 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS之深入探究CADisplayLink和NSTimer的对比和内存溢出问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

CADisplayLink的基本說明和使用

一、什么是CADisplayLink?
  • 簡單地說,它就是一個定時器,每隔幾毫秒刷新一次屏幕。
  • CADisplayLink是一個能讓我們以和屏幕刷新率相同的頻率將內容畫到屏幕上的定時器。在應用中創建一個新的 CADisplayLink 對象,把它添加到一個runloop中,并給它提供一個 target 和 selector 在屏幕刷新的時候調用。
  • 一但 CADisplayLink 以特定的模式注冊到runloop之后,每當屏幕需要刷新的時候,runloop就會調用CADisplayLink綁定的target上的selector,這時target可以讀到 CADisplayLink 的每次調用的時間戳,用來準備下一幀顯示需要的數據。
  • 在添加進runloop的時候應該選用高一些的優先級,來保證動畫的平滑。可以設想一下,在動畫的過程中,runloop被添加進來了一個高優先級的任務,那么,下一次的調用就會被暫停轉而先去執行高優先級的任務,然后在接著執行 CADisplayLink 的調用,從而造成動畫過程的卡頓,使動畫不流暢。
二、CADisplayLink屬性和方法
  • 方法
// 生成實例 +(CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;// 將實例加入到一個選定的runloop中,事件就能被觸發了。 -(void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;// 從某個runloop中移除當前實例 -(void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;// 計時器銷毀:首先是把本身(定時器)從NSRunLoop中移除,然后就是釋放對‘target’對象的強引用,從而解決定時器帶來的內存泄露問題。 -(void)invalidate;
  • 屬性
    ① duration屬性:提供了每幀之間的時間,也就是屏幕每次刷新之間的的時間。該屬性在target的selector被首次調用以后才會被賦值。selector的調用間隔時間計算方式是:時間=duration×frameInterval。 可以使用這個時間來計算出下一幀要顯示的UI的數值。但是 duration只是個大概的時間,如果CPU忙于其它計算,就沒法保證以相同的頻率執行屏幕的繪制操作,這樣會跳過幾次調用回調方法的機會。
    ② frameInterval屬性:是可讀可寫的NSInteger型值,標識間隔多少幀調用一次 selector 方法,默認值是1,即每幀都調用一次。如果每幀都調用一次的話,對于iOS設備來說那刷新頻率就是60HZ也就是每秒60次,如果將 frameInterval 設為2 那么就會兩幀調用一次,也就是變成了每秒刷新30次。
    ③ pause屬性:控制CADisplayLink的運行。當想結束一個CADisplayLink的時候,應該調用-(void)invalidate 從runloop中刪除并刪除之前綁定的 target 跟 selector;
    ④ timestamp屬性: 只讀的CFTimeInterval值,表示屏幕顯示的上一幀的時間戳,這個屬性通常被target用來計算下一幀中應該顯示的內容。

CADisplayLink 與 NSTimer 的對比

一、原理不同
  • CADisplayLink是一個能讓我們以和屏幕刷新率同步的頻率將特定的內容畫到屏幕上的定時器類。CADisplayLink 以特定模式注冊到 runloop 后,每當屏幕顯示內容刷新結束的時候,runloop就會向 CADisplayLink 指定的 target 發送一次指定的 selector 消息, CADisplayLink類對應的 selector 就會被調用一次。
  • NSTimer以指定的模式注冊到runloop后,每當設定的周期時間到達后,runloop會向指定的target發送一次指定的selector消息。
二、周期設置方式不同
  • iOS設備的屏幕刷新頻率(FPS)是60Hz,因此CADisplayLink的selector 默認調用周期是每秒60次,這個周期可以通過frameInterval屬性設置, CADisplayLink的selector每秒調用次數=60/ frameInterval。比如當 frameInterval設為2,每秒調用就變成30次。因此, CADisplayLink 周期的設置方式略顯不便。
  • NSTimer的selector調用周期可以在初始化時直接設定,相對就靈活的多。
三、精確度不同
  • iOS設備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會在每次刷新結束都被調用,精確度相當高。
  • NSTimer的精確度就顯得低了點,比如NSTimer的觸發時間到的時候,runloop如果在忙于別的調用,觸發時間就會推遲到下一個runloop周期。更有甚者,在OS X v10.9以后為了盡量避免在NSTimer觸發時間到了而去中斷當前處理的任務,NSTimer新增了tolerance屬性,讓用戶可以設置可以容忍的觸發的時間范圍。
四、使用場合
  • 從原理上不難看出, CADisplayLink 使用場合相對專一, 適合做界面的不停重繪,比如視頻播放的時候需要不停地獲取下一幀用于界面渲染。
  • NSTimer的使用范圍要廣泛的多,各種需要單次或者循環定時處理的任務都可以使用。

CADisplayLink 和 NSTimer 的使用

一、CADisplayLink的使用
  • 創建初始化
self.displayLink = [CADisplayLink displayLinkWithTarget:selfselector:@selector(handleDisplayLink:)];[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]forMode:NSDefaultRunLoopMode];
  • 停止方法
[self.displayLink invalidate];self.displayLink = nil;
  • handleDisplayLink實現:
- (void)handleDisplayLink:(CADisplayLink *)displayLink {// do something }
  • 當把CADisplayLink對象add到runloop中后,selector就能被周期性調用,類似于NSTimer被啟動了;執行invalidate操作時, CADisplayLink對象就會從runloop中移除,selector 調用也隨即停止,類似于NSTimer的invalidate方法。
二、NSTimer 的使用
  • 創建初始化
// 方法一 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:YES]; // 方法二 NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
  • 釋放
[timer invalidate];
  • 調用創建方法后,target對象的計數器會加1,直到執行完畢,自動減1。如果是循環執行的話,就必須手動關閉,否則可以不執行釋放方法。
  • 存在延遲 ,不管是一次性的還是周期性的timer的實際觸發事件的時間,都會與所加入的RunLoop和RunLoop Mode有關,如果此RunLoop正在執行一個連續性的運算,timer就會被延時出發。重復性的timer遇到這種情況,如果延遲超過了一個周期,則會在延時結束后立刻執行,并按照之前指定的周期繼續執行。

CADisplayLink 與 NSTimer 的內存泄漏

一、產生原因分析
// CADisplayLinkself.displayLink = [CADisplayLink displayLinkWithTarget:selfselector:@selector(handleDisplayLink:)];[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]forMode:NSDefaultRunLoopMode];// NSTimerNSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
  • 如上代碼NSTimer和CADisplayLink都需要加入到NSRunloop里面才能生效,NSRunloop強引用了NSTimer和CADisplayLink對象,同時NSTimer和CADisplayLink對象又把self設置成了自己的target,于是它們強引用了self,因為self一直被Runloop強引用所以就釋放不了,造成內存泄漏。
二、解決方案
  • 在對象dealloc之前使用invalidate方法停止Timer,這樣Timer就會被釋放。不會造成內存泄漏。但是如果想讓Timer一直運行直到對象被dealloc的時候才被停止,顯然這個方法并不適用,因為如果不調用invalidate方法,對象根本不會被銷毀,deallco方法根本不會執行。
  • 為了滿足在對象銷毀的時候停止定時器的需求,還有一種方案就是替換target,比較常見的是讓NSTimer類自己作為target,配合block傳遞需要執行的方法。使用的時候需要注意block的循環引用問題,在閉包中使用self需要改為weak引用。
#import "NSTimer+YDWTimerTarget.h"@implementation NSTimer (YDWTimerTarget)+ (NSTimer *)ydw_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeat:(BOOL)yesOrNo block:(void (^)(NSTimer *))block{return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(startTimer:) userInfo:[block copy] repeats:yesOrNo]; }+ (void)startTimer:(NSTimer *)timer {void (^block)(NSTimer *timer) = timer.userInfo;if (block) {block(timer);} } @end
  • (void)timerWithTimeInterval: repeats: block:支持了這種block的形式,完全處理了這種內存泄漏的情況。
  • CADisplayLink的內存泄漏的產生和NSTimer類似。

總結

以上是生活随笔為你收集整理的iOS之深入探究CADisplayLink和NSTimer的对比和内存溢出问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。