NSTimer導致誤差的原因:
1、NSTimer加在main runloop中,模式是NSDefaultRunLoopMode,main負責所有主線程事件,例如UI界面的操作,復雜的運算,這樣在同一個runloop中timer就會產(chǎn)生阻塞。
2、模式的改變。主線程的 RunLoop 里有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。
當你創(chuàng)建一個 Timer 并加到 DefaultMode 時,Timer 會得到重復回調(diào),但此時滑動一個ScrollView時,RunLoop 會將 mode 切換為 TrackingRunLoopMode,這時 Timer 就不會被回調(diào),并且也不會影響到滑動操作。所以就會影響到NSTimer不準的情況。
PS:DefaultMode 是 App 平時所處的狀態(tài),rackingRunLoopMode 是追蹤 ScrollView 滑動時的狀態(tài)。
解決的方法,
1、在主線程中進行NSTimer操作,但是將NSTimer實例加到main runloop的特定mode(模式)中。避免被復雜運算操作或者UI界面刷新所干擾。
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
2、在子線程中進行NSTimer的操作,再在主線程中修改UI界面顯示操作結(jié)果;
- (void)timerMethod2 {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[thread start];
}
- (void)newThread
{
@autoreleasepool
{
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
}
一開始的時候系統(tǒng)就為我們將主線程的main runloop隱式的啟動了。
在創(chuàng)建線程的時候,可以主動獲取當前線程的runloop。每個子線程對應一個runloop
3.GCD定時器不受RunLoop約束,比NSTimer更加準時
@interface ViewController ()
/** 定時器(這里不用帶*,因為dispatch_source_t就是個類,內(nèi)部已經(jīng)包含了*) */
@property (nonatomic, strong) dispatch_source_t timer;
@end
int count = 0;
// 獲得隊列
dispatch_queue_t queue = dispatch_get_main_queue();
// 創(chuàng)建一個定時器(dispatch_source_t本質(zhì)還是個OC對象)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 設置定時器的各種屬性(幾時開始任務,每隔多長時間執(zhí)行一次)
// GCD的時間參數(shù),一般是納秒(1秒 == 10的9次方納秒)
// 何時開始執(zhí)行第一個任務
// dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC) 比當前時間晚3秒
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer, start, interval, 0);
// 設置回調(diào)
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"------------%@", [NSThread currentThread]);
count++;
if (count == 4) {
// 取消定時器
dispatch_cancel(self.timer);
self.timer = nil;
}
});
// 啟動定時器
dispatch_resume(self.timer);