
NSTimer
- NSTimer與scrollview
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateUI) userInfo:nil repeats:YES];
這種最簡(jiǎn)單方便但是這種方法創(chuàng)建的定時(shí)器如果和scrollview一起使用便會(huì)出現(xiàn)在滾動(dòng)scrollview時(shí)導(dǎo)致定時(shí)器不執(zhí)行的問(wèn)題, 之所以會(huì)出現(xiàn)這種情況是RunLoop在作怪, 當(dāng)你創(chuàng)建一個(gè)NSTimer時(shí), 這個(gè)定時(shí)器默認(rèn)是被加入到當(dāng)前默認(rèn)的線程, 也就是主線程。 而主線程默認(rèn)的RunLoop的模式是NSDefaultRunLoopMode, 當(dāng)進(jìn)行滾動(dòng)scrollview滾動(dòng)時(shí), RunLoop會(huì)切換到TrackingRunLoopMode 模式,追蹤 ScrollView 滑動(dòng)時(shí)的狀態(tài)。當(dāng)你創(chuàng)建一個(gè) Timer 并加到 DefaultMode 時(shí),Timer 會(huì)得到重復(fù)回調(diào),但此時(shí)滑動(dòng)一個(gè)TableView時(shí),RunLoop 會(huì)將 mode 切換為 TrackingRunLoopMode,這時(shí) Timer 就不會(huì)被回調(diào),并且也不會(huì)影響到滑動(dòng)操作。
既然是因?yàn)閙ode的切換導(dǎo)致Timer無(wú)法被回調(diào), 那就需要把Timer放到能夠被回調(diào)的mode里面。
修改代碼
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateUI) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//NSRunLoopCommonModes的意思為,定時(shí)器可以運(yùn)行在標(biāo)記為common modes模式下。
//具體包括兩種: kCFRunloopDefaultMode 和 UITrackingRunloopMode。
[timer fire];
這樣就可以了, 在滑動(dòng)scrollview時(shí), 不會(huì)導(dǎo)致定時(shí)器無(wú)法回調(diào)的問(wèn)題
-
NSTimer釋放
- 操作定時(shí)器的[self.timer invalidate]方法,不能期望在dealloc中停止定時(shí)器,因?yàn)槎〞r(shí)器和dealloc會(huì)互相等待,定時(shí)器如果不執(zhí)行[self.timer invalidate]方法,根本不會(huì)進(jìn)dealloc反法, 導(dǎo)致內(nèi)存泄露。一種優(yōu)雅的釋放定時(shí)器的方式
假如一個(gè)Controller里面含有一個(gè)定時(shí)器,那么當(dāng)這個(gè)Controller已經(jīng) 出棧,即被執(zhí)行了pop,此時(shí)如果定時(shí)器并沒(méi)有結(jié)束時(shí),該Controller不會(huì)馬上被銷(xiāo)毀,只有當(dāng)定時(shí)器執(zhí)行完畢,才會(huì)走dealloc方法
子線程中使用NSTimer要手動(dòng)開(kāi)啟RunLoop
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(subThread) object:nil];
[thread start];
- (void)subThread {
// **** 無(wú)論在ARC還是在MRC下,都需要在子線程中添加自動(dòng)釋放池.因?yàn)橄到y(tǒng)自動(dòng)幫助主線程創(chuàng)建了自動(dòng)釋放池,而沒(méi)有幫子線程創(chuàng)建自動(dòng)釋放池,所以需要我們手動(dòng)創(chuàng)建.
//在每次NSRunloop休眠前清理自動(dòng)釋放池。
@autoreleasepool {
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
//如果需要聲明屬性, 記得一定要把賦值寫(xiě)在開(kāi)啟RunLoop之前, 否則定時(shí)器則無(wú)法停止
_timer = timer;
//子線程RunLoop默認(rèn)是關(guān)閉的
// 不需要手動(dòng)的區(qū)創(chuàng)建runloop, 只需要獲取到,然后調(diào)用run來(lái)開(kāi)啟事件處理循環(huán).
[[NSRunLoop currentRunLoop] run];
}
}
CADisplayLink
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(disPlay)];
//與NSTimer不同, 需要手動(dòng)添加到RunLoop中, 如果添加的模式是NSDefaultRunLoopMode, 則scrollview滾動(dòng)時(shí), 會(huì)阻礙定時(shí)器的執(zhí)行。
//如果是NSRunLoopCommonModes, 則不會(huì)阻礙。
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//設(shè)置刷新頻率, 默認(rèn)是每秒60幀, 如果設(shè)置成60就每秒執(zhí)行一次
//但是實(shí)際測(cè)試卻發(fā)現(xiàn),設(shè)置60是2秒執(zhí)行一次, 設(shè)置成30才是每秒執(zhí)行一次。
displayLink.frameInterval = 60;
關(guān)閉定時(shí)器// [displayLink invalidate];
GCD
dispatch source是一個(gè)監(jiān)視某些類(lèi)型事件的對(duì)象。當(dāng)這些事件發(fā)生時(shí),它自動(dòng)將一個(gè)block放入一個(gè)dispatch queue的執(zhí)行例程中。
/**
* 創(chuàng)建dispatch源
*
* @param DISPATCH_SOURCE_TYPE_TIMER 事件源的類(lèi)型
* @param 0 <#0 description#>
* @param 0 <#0 description#>
* @param dispatch_get_main_queue 在哪個(gè)線程上執(zhí)行
*
* @return dispatch_source_t
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
/**
* @param start 控制計(jì)時(shí)器第一次觸發(fā)的時(shí)刻
* @param interval 每隔多長(zhǎng)時(shí)間執(zhí)行一次
* @param leeway 誤差值,0表示最小誤差,值越小性能消耗越大
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
/**
* 事件處理的回調(diào)
*/
dispatch_source_set_event_handler(timer, ^{
//取消定時(shí)器
dispatch_cancel(timer);
});
/**
* Dispatch source啟動(dòng)時(shí)默認(rèn)狀態(tài)是掛起的,我們創(chuàng)建完畢之后得主動(dòng)恢復(fù),否則事件不會(huì)被傳遞,也不會(huì)被執(zhí)行
*/
dispatch_resume(timer);
dispatch_source_t需要被引用計(jì)數(shù), 否則定時(shí)器引用計(jì)數(shù)為0, dispatch_source_t會(huì)被系統(tǒng)收回.
GCD定時(shí)器的優(yōu)點(diǎn)有很多,首先不受Mode的影響,而NSTimer受Mode影響時(shí)常不能正常工作,除此之外GCD的精確度明顯高于NSTimer,這些優(yōu)點(diǎn)讓我們有必要了解GCD定時(shí)器這種方法。
但是GCD也并非是非常準(zhǔn)確的, GCD文檔上有一句這樣的話
Note: Even if you specify a leeway value of 0, you should never expect a timer to fire at the exact nanosecond you requested. The system does its best to accommodate your needs but cannot guarantee exact firing times.
翻譯
注意: 即使您指定的回程值為0,也永遠(yuǎn)不要期望計(jì)時(shí)器在您要求的確切納秒時(shí)觸發(fā)。該系統(tǒng)會(huì)盡力滿足您的需求,但不能保證準(zhǔn)確的點(diǎn)火時(shí)間。