關于NSTimer在使用過程中,會出現不精準的情況,如下:
1、NSTimer加在main runloop中,模式是NSDefaultRunLoopMode,main負責所有主線程事件,例如UI界面的操作,複雜的運算,這樣在同一個runloop中timer就會産生阻塞。
2、模式的改變。主線程的 RunLoop 裡有兩個預置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。
當你建立一個 Timer 并加到 DefaultMode 時,Timer 會得到重複回調,但此時滑動一個ScrollView時,RunLoop 會将 mode 切換為 TrackingRunLoopMode,這時 Timer 就不會被回調,并且也不會影響到滑動操作。是以就會影響到NSTimer不準的情況。
(DefaultMode 是 App 平時所處的狀态,rackingRunLoopMode 是追蹤 ScrollView 滑動時的狀态。)
處理這下問題,其實并不難,小編為你提供三種方式:
方法一:
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界面顯示操作結果;
- (
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];
}
}
總結:
一開始的時候系統就為我們将主線程的main runloop隐式的啟動了。
在建立線程的時候,可以主動擷取目前線程的runloop。每個子線程對應一個runloop
方法二:
使用示例
使用mach核心級的函數可以使用mach_absolute_time()擷取到CPU的tickcount的計數值,可以通過”mach_timebase_info”函數擷取到納秒級的精确度 。然後使用mach_wait_until(uint64_t deadline)函數,直到指定的時間之後,就可以執行指定任務了。
關于資料結構mach_timebase_info的定義如下:
struct mach_timebase_info {uint32_t numer;uint32_t denom;};
#
include
#
include
static
const
uint64_t NANOS_PER_USEC = 1000ULL;
static
const
uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;
static
const
uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;
static
mach_timebase_info_data_t timebase_info;
static
uint64_t nanos_to_abs(uint64_t nanos) {
return
nanos * timebase_info.denom / timebase_info.numer;
}
void
example_mach_wait_until(
int
seconds)
{
mach_timebase_info(&timebase_info);
uint64_t time_to_wait = nanos_to_abs(seconds * NANOS_PER_SEC);
uint64_t now = mach_absolute_time();
mach_wait_until(now + time_to_wait);
}
方法三:直接使用GCD替代!
//建立一個 time 并放到隊列中
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
//需要強引用否則 time會銷毀,無法繼續執行
self.timer = timer;
//首次執行時間 間隔時間 時間精度
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"----");
});
//激活 timer
dispatch_resume(timer);