簡述:一個(gè)在確定時(shí)間間隔內(nèi)執(zhí)行一次或者多次我們指定對(duì)象方法的對(duì)象荒给。
如果是多次執(zhí)行對(duì)象方法萤捆,那么它怎么保證執(zhí)行的時(shí)候?qū)ο蟛槐会尫拍兀?/p>
問題:
1.你知道NSTimer會(huì)retain你添加調(diào)用方法的對(duì)象嗎吝镣?
2.你知道NSTimer并不是每次都準(zhǔn)確按你設(shè)定的時(shí)間間隔來觸發(fā)嗎徙瓶?
3.NSTimer需要和NSRunLoop結(jié)合起來使用击费,你知道是怎么結(jié)合使用的嗎至扰?
4.你知道除了用NSTimer實(shí)現(xiàn)定時(shí)器,還有別的方式也能實(shí)現(xiàn)定時(shí)器嗎茬暇?
大綱:
1.定時(shí)器的應(yīng)用場(chǎng)景
2.定時(shí)器的常用方式
3.fire方法的正確理解
4.NSRunLoopMode對(duì)定時(shí)器的影響
5.定時(shí)器一定會(huì)在規(guī)定時(shí)間間隔內(nèi)實(shí)時(shí)出發(fā)事件嗎首昔?
6.定時(shí)器引起的循環(huán)引用
7.子線程開啟定時(shí)器
8.GCD定時(shí)器
一:定時(shí)器的使用
第一種:
_timer = [NSTimer timerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];//需要手動(dòng)把定時(shí)器加入到RunLoop里面才會(huì)生效
第二種:
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES]; //系統(tǒng)會(huì)自動(dòng)加到RunLoop里面
注:iOS10之后增加了block方式的定時(shí)器初始化,為了避免之前方式所引起的循環(huán)引用問題:timer和scheduled都有:
(這樣就會(huì)消除循環(huán)引用糙俗,正常調(diào)用dealloc方法了)
__weak typeof(self) weakSelf = self;
_timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
// NSLog(@"blockAction");
[weakSelf timerAction];
}];
實(shí)時(shí)觸發(fā)定時(shí)器勒奇,調(diào)用定時(shí)器方法,而且不會(huì)影響timer的響應(yīng)周期
[_timer fire];
定時(shí)器銷毀的唯一方法:
[_timer invalidate];
注意點(diǎn)1:子線程上的定時(shí)器巧骚,請(qǐng)?jiān)谧泳€程銷毀
[NSThread detachNewThreadSelector:@selector(ThreadTimer) toTarget:self withObject:nil];
-
(void)ThreadTimer {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];//子線程上的定時(shí)器赊颠,必須要在子線程里面invalidate格二,否則會(huì)造成資源的浪費(fèi)
[self performSelector:@selector(invalidateTimer) withObject:nil afterDelay:3.f];
//RunLoop在子線程上,是需要你手動(dòng)開啟竣蹦,RunLoop
//RunLoop 的創(chuàng)建 [NSRunLoop currentRunLoop],當(dāng)獲取當(dāng)前RunLoop的時(shí)候顶猜,系統(tǒng)就已經(jīng)自動(dòng)創(chuàng)建好了,并不是alloc
[[NSRunLoop currentRunLoop] run];
NSLog(@"我在學(xué)習(xí)中");//當(dāng)RunLoop結(jié)束 才會(huì)執(zhí)行下面的語句
}
注意點(diǎn)2:注意定時(shí)器的線程里痘括,不要做復(fù)雜的計(jì)算操作长窄,這樣會(huì)導(dǎo)致定時(shí)器不準(zhǔn)確
GCD定時(shí)器:
//內(nèi)存問題:當(dāng)timeTwo(且eventhandle里沒有包含timer) 為成員變量的時(shí)候 沒問題;當(dāng)為局部變量的時(shí)候纲菌,只運(yùn)行一次
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = dispatch_queue_create("gcd實(shí)現(xiàn)定時(shí)器", 0);
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.f * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
// timer;//引用計(jì)數(shù)挠日,不會(huì)被釋放
// if (!repeats) {
// dispatch_cancel(timer);
// }else{
[weakSelf timerAction];
// }
});
dispatch_resume(timer);
gcd定時(shí)器的銷毀:dispatch_cancel(timer);
Display定時(shí)器:
創(chuàng)建:
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timerAction)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
停止:
[displayLink invalidate];
①屏幕刷新時(shí)調(diào)用
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)用一次椅贱。所以通常情況下懂算,按照iOS設(shè)備屏幕的刷新率60次/秒
②延遲
iOS設(shè)備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會(huì)在每次刷新結(jié)束都被調(diào)用夜涕,精確度相當(dāng)高犯犁。但如果調(diào)用的方法比較耗時(shí),超過了屏幕刷新周期女器,就會(huì)導(dǎo)致跳過若干次回調(diào)調(diào)用機(jī)會(huì)酸役。
如果CPU過于繁忙,無法保證屏幕60次/秒的刷新率驾胆,就會(huì)導(dǎo)致跳過若干次調(diào)用回調(diào)方法的機(jī)會(huì)涣澡,跳過次數(shù)取決CPU的忙碌程度。
③使用場(chǎng)景
從原理上可以看出丧诺,CADisplayLink適合做界面的不停重繪入桂,比如視頻播放的時(shí)候需要不停地獲取下一幀用于界面渲染。