定時(shí)器是iOS開(kāi)發(fā)中頻繁使用的開(kāi)發(fā)技能,存在多種創(chuàng)建方式,可根據(jù)實(shí)際需求選用;
iOS中的定時(shí)器大致分為這幾類:
- NSTimer
- CADisplayLink
- GCD定時(shí)器
NSTimer
NSTimer是最常用的定時(shí)器創(chuàng)建方式,比較常用的創(chuàng)建方法有以下兩種:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
兩種方法創(chuàng)建的定時(shí)器區(qū)別在于以下點(diǎn):
-
scheduledTimerWithTimeInterval在主線程創(chuàng)建的定時(shí)器會(huì)在創(chuàng)建后自動(dòng)將 timer
添加到主線程的 runloop 并啟動(dòng),主線程的 runloopMode 為NSDefaultRunLoopMode,但是在 ScrollView 滑動(dòng)時(shí)執(zhí)行的是UITrackingRunLoopMode,NSDefaultRunLoopMode被掛起,定時(shí)器失效,等到停止滑動(dòng)才恢復(fù);
因此需要將 timer 分別加入UITrackingRunLoopMode和NSDefaultRunLoopMode中:
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop mainRunLoop] addTimer:timer forMode: UITrackingRunLoopMode];
或者直接添加到 NSRunLoopCommonModes 中:
[[NSRunLoop mainRunLoop] addTimer:timer forMode: NSRunLoopCommonModes];
也可新開(kāi)一個(gè)子線程,主線程的 runloop 是自動(dòng)開(kāi)啟的,但子線程的 runloop 需要手動(dòng)開(kāi)啟,代碼如下:
__block NSInteger count = 0;
[NSThread detachNewThreadWithBlock:^{
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
count++;
NSLog(@"第%li次", count);
}];
[[NSRunLoop currentRunLoop] run];
}];
如果用 timerWithTimeInterval 創(chuàng)建則需要手動(dòng)添加到 runloop 中;
-
timerWithTimeInterval創(chuàng)建的定時(shí)器不會(huì)直接啟動(dòng),而需要手動(dòng)添加到 runloop 中;為防止出現(xiàn)滑動(dòng)視圖時(shí)定時(shí)器被掛起,可直接添加到NSRunLoopCommonModes;
CADisplayLink
什么是 CADisplayLink
CADisplaylink 是一個(gè)計(jì)時(shí)器對(duì)象,可以使用這個(gè)對(duì)象來(lái)保持應(yīng)用中的繪制與顯示刷新的同步。更通俗的講,電子顯示屏都是由一個(gè)個(gè)像素點(diǎn)構(gòu)成,要讓屏幕顯示的內(nèi)容變化,需要以一定的頻率刷新這些像素點(diǎn)的顏色值,系統(tǒng)會(huì)在每次刷新時(shí)觸發(fā) CADisplaylink。
CADisplayLink的使用
// CADisplayLink
- (void)sa_displayLink {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkAction)];
[_displayLink setFrameInterval:50]; //設(shè)置 CADisplayLink selector 調(diào)用時(shí)間間隔
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 添加到 runloop,并指定 RunloopMode;
}
// CADisplayLink selector 調(diào)用
- (void)displayLinkAction {
static NSInteger count = 0;
count++;
NSLog(@"第%li次調(diào)用", count);
}
下面是 setFrameInterval 分別設(shè)置 5 和 50的打印結(jié)果,可看出selector調(diào)用時(shí)間間隔不同:


調(diào)用
setPaused:可以暫停、開(kāi)啟 CADisplayLink;
[_displayLink setPaused:YES];
停止使用 CADisplayLink,調(diào)用 invalidate;
有些需求配合使用 CADisplayLink 可以更簡(jiǎn)便實(shí)現(xiàn);
GCD定時(shí)器
直接貼一個(gè)demo方法看看使用:
- (void)gcdTimerDemoMethod {
__block int count = 0;
__block dispatch_source_t gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); //創(chuàng)建定時(shí)器,并指定線程,dispatch_source_t 本質(zhì)上也是一個(gè)OC對(duì)象;
dispatch_source_set_timer(gcdTimer, DISPATCH_TIME_NOW, (uint64_t)(1.0 * NSEC_PER_SEC), 0); //設(shè)置定時(shí)器間隔時(shí)間
//設(shè)置定時(shí)器 action
dispatch_source_set_event_handler(gcdTimer, ^{
NSLog(@"------------%@", [NSThread currentThread]);
count++;
if (count == 4) {
// 取消定時(shí)器
dispatch_cancel(gcdTimer);
gcdTimer = nil;
}
});
dispatch_resume(gcdTimer); //啟動(dòng)定時(shí)器
}
//定時(shí)器釋放
- (void)dealloc {
dispatch_source_cancel(_timer);
dispatch_cancel(_timer);
_timer = nil;
}
GCD 定時(shí)器比 NSTimer 更精確,定時(shí)器一定要被強(qiáng)引用,不然會(huì)被釋放,導(dǎo)致的定時(shí)器無(wú)效;