iOS 處理NSTimer不准問題的三種解決方案


在開發工作中,我們經常會用到NSTimer 來實現定時器功能。但用心留意的小伙伴兒應該注意到了一個問題:定時器受其他UI界面刷新或者手勢問題會出現卡頓現象,這會導致定時器不准確。究其原因:就是線程等待導致的問題。這也是面試中的一個經典案例。

具體原因:NSTimer 的runloop類型是NSDefaultRunloopMode 主線程中, 界面的刷新也在主線程中,UIScrollview滑動的過程中是在NSTrackingRunloopMode中,當我們在手指滑動過程中,系統會將NSDefaultRunloopMode 更改為NSTrackingRunloopMode,所以這也是為什么會出現NSTimer短暫暫停的原因

目前的解決方案有三種:

1.通過更改RunloopMode 避免主線程阻塞

因為要避免主線程阻塞 所以也就是要避免使用NSDefaultRunloopMode。
查看到runloopMode的類型,可以看到 NSCommonRunloopMode (偽模式)不常用,而且是作為一個通用存在的。所以我們只需將NSDefaultRunloopMode 改為NSCommonRunloopMode調用就可以了。

但有一點需要注意:如果有多個NSCommonRunloopMode 執行不同任務 還是會出現卡頓導致定時器不准確問題。

self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

2.將NSTimer放在子線程中,當需要刷新UI界面時 再在主線程中進行。

這個方法是類似於將NSTimer 任務獨立出來,也就是將定時和刷新UI的工作任務分開來。

- (void)timerMethod2 {

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];

[thread start];

}

 -(void)newThread

{

[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(showTime) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] run];

}

這個方法是可以實現但會增加代碼量,而且有一定的內存泄漏風險。
因為定時器的取消我們在delloc中進行的。如果對象沒有完全銷毀 就會導致內存泄漏問題出現。
所以比較推崇接下來的第三種方案。

3.gcd實現定時器

gcd完全不受runloop影響,也就是說不會收到手勢和UI刷新影響。所以它比NSTimer 更加准確。

 int count = 0;

     // 獲得隊列
    dispatch_queue_t queue = dispatch_get_main_queue();

    // 創建一個定時器(dispatch_source_t本質還是個OC對象)
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

    // 設置定時器的各種屬性(幾時開始任務,每隔多長時間執行一次)
    // GCD的時間參數,一般是納秒(1秒 == 10的9次方納秒)
    // 何時開始執行第一個任務
    // 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);

    // 設置回調
    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);
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM