天天看點

精準+正确的使用NSTimer

關于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);