編寫App工程中,會遇到各種定時器的問題:比如某個頁面上的數(shù)據(jù)定時刷新,需要在定時器中反復(fù)向服務(wù)器發(fā)送請求;首頁廣告位(Banner)的圖片不斷滾動,需要在定時器中設(shè)定變換的間隔時間;收到用戶手勢交互時,出現(xiàn)響應(yīng)動畫,可以通過定時器設(shè)置不同動畫連續(xù)執(zhí)行等等。iOS中的Timer可以通過三種方式來實現(xiàn):NSTimer,dispatch,CADisplayLink,其執(zhí)行的精確度依次提高。下面介紹一下各自的使用方式。

NSTimer
NSTimer是OC以面向?qū)ο蠓绞椒庋b的Timer對象,從其類文檔中可以看到它的兩種創(chuàng)建方式:timer和scheduledTimer。
self.timer = [NSTimer timerWithTimeInterval:2 repeats:true block:^(NSTimer * _Nonnull timer)
NSLog(@"timer");
}];
self.timer = [NSTimer timerWithTimeInterval:2 repeats:true block:^(NSTimer * _Nonnull timer)
NSLog(@"timer");
}];
[self.timer fire];
[self.timer invalidate];
timerWithTimeInterval創(chuàng)建的timer, 在fire后喚醒;
scheduledTimerWithTimeInterval創(chuàng)建的timer, 創(chuàng)建后立即喚醒;
timer在invalidate后停止。
dispatch
利用多線程GCD創(chuàng)建的Timer,精確度更高,也可以通過參數(shù)設(shè)置Timer首次執(zhí)行時間。
__block int count = 3;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//通過start參數(shù)控制第一次執(zhí)行的時間,DISPATCH_TIME_NOW表示立即執(zhí)行
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"dispatch_source_set_timer start");
NSLog(@"%zd", count);
if (count == 0) {
dispatch_source_cancel(timer);
}
count--;
});
NSLog(@"main queue");
dispatch_resume(timer);
CADisplayLink
CADisplayLink是和iOS界面刷新效率同步執(zhí)行,可以在1s內(nèi)執(zhí)行60次,執(zhí)行效率最高。如果屏幕滑動時卡頓,可以用它來檢測屏幕屏幕刷新頻率。當(dāng)然,不能在其執(zhí)行方法中加載大量任務(wù),否則手機內(nèi)存會急劇增高。
@property (nonatomic, strong) CADisplayLink *link;
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, assign) NSTimeInterval lastTime;
@property (nonatomic, assign) NSInteger count;
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- (void)tick: (CADisplayLink *)link {
if (self.lastTime == 0) {
self.lastTime = link.timestamp;
return;
}
self.count++;
NSLog(@"%f", link.timestamp);
NSTimeInterval delta = link.timestamp - self.lastTime;
if (delta < 1) return;
self.lastTime = link.timestamp;
float fps = _count / delta;
self.label.text = [NSString stringWithFormat:@"%0f", fps];
self.count = 0;
}
Tips:NSRunLoop
iOS應(yīng)用執(zhí)行的方式,采用了RunLoop的消息循環(huán),簡要來說啟動后整個應(yīng)用就進(jìn)入一個死循環(huán),應(yīng)用中的所有事件(各種手勢,交互功能(Target-Event)等)在死循環(huán)中被檢測后加入到消息隊列,然后不斷執(zhí)行。
每一線程中都有一個RunLoop,只不過子線程中的RunLoop默認(rèn)關(guān)閉。通過[[NSRunLoop currentRunLoop] run]的方法可以喚醒子線程的RunLoop。
RunLoop中的模式有兩種: kCFRunLoopDefaultMode和UITrackingRunLoopMode。kCFRunLoopDefaultMode也即是iOS開發(fā)文檔中的NSDefaultRunLoopMode,NSRunLoopCommonModes是上述兩種mode的組合。主線程的RunLoop在UIScrollView滑動時會切換成UITrackingRunLoopMode,如果將Timer添加到NSDefaultRunLoopMode,就會在滑動手勢下停止執(zhí)行。
關(guān)注和喜歡都是對我的鼓勵和支持~