官方API的定義是:
A timer object that allows your application to synchronize its drawing to the refresh rate of the display.
CADisplayLink是一個定時器對象,它可以讓你與屏幕刷新頻率相同的速率來刷新你的視圖。就說CADisplayLink是用于同步屏幕刷新頻率的計時器脆栋。
初始化
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel
添加到runloop风题,不然不會被觸發(fā)
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode嘁扼。
? ? ? ? 每個計時器對象只能加入到一個runloop中宙橱,但是可以被添加到不同的mode中捻悯,當(dāng)displayLink被加入到runloop時憔杨,會被runloop隱式retain鸟赫。
CADisplayLink的方法和屬性
- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode;
把指定mode的displayLink對象從runloop中移除。這個實(shí)例對象適用于想要改變實(shí)例對象的mode消别,這時候就要先從當(dāng)前mode中remove抛蚤,然后再add。這個方法會對計時器進(jìn)行隱式的release妖啥。在調(diào)用該方法時霉颠,需要做判斷,如果當(dāng)期計時器不在runloop的話荆虱,會出現(xiàn)野指針的crash蒿偎。出現(xiàn)crash的原因是runloop多次調(diào)用了release方法,進(jìn)行了over-release怀读。
- (void)invalidate;
把displayLink對象從所有runloop modes中移除诉位。執(zhí)行這個操作之后,displayLink對象就沒有加入任何的runloop菜枷,這樣回調(diào)也不會在每次刷新屏幕的時候執(zhí)行苍糠。
@property(getter=isPaused, nonatomic) BOOL paused;
paused可以用于暫停和開啟的設(shè)置。
@property(readonly, nonatomic) CFTimeInterval duration;
只讀的CFTimeInterval值啤誊,表示兩次屏幕刷新之間的時間間隔岳瞭。需要注意的是拥娄,該屬性在target的selector被首次調(diào)用以后才會被賦值。不過需要說明的一點(diǎn)是瞳筏,如果CPU過于繁忙稚瘾,duration的值是會浮動的。
@property(readonly, nonatomic) CFTimeInterval timestamp;
只讀的CFTimeInterval值姚炕,表示屏幕顯示的上一幀的時間戳摊欠,這個屬性通常被target用來計算下一幀中應(yīng)該顯示的內(nèi)容。需要注意的是柱宦,該屬性在target的selector被首次調(diào)用以后才會被賦值些椒。
@property(nonatomic) NSInteger frameInterval API_DEPRECATED("preferredFramesPerSecond", ios(3.1, 10.0), watchos(2.0,3.0), tvos(9.0,10.0));
事件觸發(fā)間隔,iOS10以后被廢棄掸刊,用preferredFramesPerSecond代替免糕。是指兩次selector觸發(fā)之間間隔幾次屏幕刷新,默認(rèn)值為1痒给,即每幀都調(diào)用一次selector说墨,這個也可以間接用來控制動畫速度。selector的調(diào)用間隔時間計算方式是:時間=duration×frameInterval苍柏。官方文檔中強(qiáng)調(diào)尼斧,當(dāng)該值被設(shè)定小于1時,結(jié)果是不可預(yù)知的试吁。
@property(nonatomic)NSInteger preferredFramesPerSecond ?API_AVAILABLE(ios(10.0), watchos(3.0), tvos(10.0));
iOS10以后的屬性棺棵,這個屬性說明了間隔多少幀調(diào)用一次回調(diào)。默認(rèn)是0熄捍,即每幀都調(diào)用回調(diào)烛恤,換句話說,就是一秒回調(diào)60次余耽。如果這個屬性值設(shè)為2缚柏,那么就是每兩幀回調(diào) 一次,也就是每秒回調(diào)30次碟贾。
實(shí)例代碼
#import "TestViewController.h"
@interface TestViewController ()
@property (nonatomic, strong) CADisplayLink *displayLink;
@end
@implementationTestViewController
- (void)viewDidLoad {
? ? [super viewDidLoad];
? ? self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(refreshEvent)];
? ? [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
? ? if(@available(iOS10.0, *)) {
? ? ? ? self.displayLink.preferredFramesPerSecond = 1;
? ? }else{
? ? ? ? self.displayLink.frameInterval=60;
? ? }
}
- (void)refreshEvent{
? ? NSLog(@"refreshEvent");
}
- (void)dealloc{
? ? NSLog(@"dealloc");
}
- (void)viewWillDisappear:(BOOL)animated{
? ? [super viewWillDisappear:animated];
? ? [self.displayLink invalidate];
? ? self.displayLink = nil;
}
@end
CADisplayLink優(yōu)勢:
依托于設(shè)備屏幕刷新頻率觸發(fā)事件币喧,所以其觸發(fā)時間上是最準(zhǔn)確的。也是最適合做UI不斷刷新的事件袱耽,過渡相對流暢杀餐,無卡頓感。
缺點(diǎn):
由于依托于屏幕刷新頻率朱巨,若果CPU不堪重負(fù)而影響了屏幕刷新史翘,那么觸發(fā)事件也會受到相應(yīng)影響。?
selector觸發(fā)的時間間隔只能是duration的整倍數(shù)。?
selector事件如果大于其觸發(fā)間隔就會造成掉幀現(xiàn)象琼讽。
另外?CADisplayLink?不能被繼承必峰。
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è)定的周期時間到達(dá)后功炮,runloop會向指定的target發(fā)送一次指定的selector消息溅潜。
2.周期設(shè)置方式不同
iOS10以前,CADisplayLink的selector?默認(rèn)調(diào)用周期是:時間=duration×frameInterval薪伏。iOS10以后滚澜,用preferredFramesPerSecond屬性代替,說明了間隔多少幀調(diào)用一次回調(diào)嫁怀。因此设捐,?CADisplayLink?周期的設(shè)置方式略顯不便。
NSTimer的selector調(diào)用周期可以在初始化時直接設(shè)定塘淑,相對就靈活的多萝招。
3、精確度不同
iOS設(shè)備的屏幕刷新頻率是固定的存捺,CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用槐沼,精確度相當(dāng)高。
NSTimer的精確度就顯得低了點(diǎn)捌治,比如NSTimer的觸發(fā)時間到的時候岗钩,runloop如果在阻塞狀態(tài),觸發(fā)時間就會推遲到下一個runloop周期肖油。
4兼吓、使用場景
CADisplayLink使用場合相對專一,適合做UI的不停重繪森枪,比如自定義動畫引擎或者視頻播放的渲染视搏。
NSTimer的使用范圍要廣泛的多,各種需要單次或者循環(huán)定時處理的任務(wù)都可以使用疲恢。
后記:
CADisplayLink和NSTimer一樣凶朗,都要記得合適的時間invalidate,避免內(nèi)存泄漏显拳,這里采用了block的方法棚愤。
@class CADisplayLink;
typedefvoid(^executeDisplayLinkBlock) (CADisplayLink*displayLink);
@interfaceCADisplayLink (Block)
@property(nonatomic,copy)executeDisplayLinkBlock executeBlock;
+ (CADisplayLink*)displayLinkWithExecuteBlock:(executeDisplayLinkBlock)block;
@end
#import "CADisplayLink+Block.h"
#import?<objc/runtime.h>
@implementationCADisplayLink (Block)
- (void)setExecuteBlock:(executeDisplayLinkBlock)executeBlock {
? ? objc_setAssociatedObject(self, @selector(executeBlock), [executeBlock copy], OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (executeDisplayLinkBlock)executeBlock {
? ? return objc_getAssociatedObject(self, @selector(executeBlock));
}
+ (CADisplayLink*)displayLinkWithExecuteBlock:(executeDisplayLinkBlock)block{
? ? CADisplayLink*displayLink = [CADisplayLink displayLinkWithTarget:self?selector:@selector(executeDisplayLink:)];
? ? displayLink.executeBlock= [block copy];
? ? return?displayLink;
}
+ (void)executeDisplayLink:(CADisplayLink*)displayLink{
? ? if(displayLink.executeBlock) {
?? ? ? displayLink.executeBlock(displayLink);
? ? }
}
@end