定时器,相信大家都不会陌生,开发中经常会遇到,最常见的广告页、计时等。当然了这些都不是大问题,但是有时候如果注意的话,相信大家都会遇到一个问题,定时器好好的执行着我们的一些操作的时候,如果我们这时候有UI操作(比如滚动界面),我们会发现好像没定时器一样,但是UI操作结束时,定时器又会重新工作。相信大家第一时间都会想到RunLoop。
说到定时器失效谈到了RunLoop,那我们先来创建定时器。其实创建定时器的方法有好几种,这里我们所以下做常用到的三种方法。其实也可以说成是两种,NSTimer分成了类方法创建和对象方法创建。
1、
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%ld",++self.page);
}];
这种类方法创建定时的一点好处就是不用我们把定时器添加到RunLoop上,但是好像跟今天的这个问题就解决不了了。(好像是的可能这点我也理解的不太清楚,因为是类,但是添加RunLoop上的是NSTimer的对象)。
2、用NSTimer的对象方法创建
NSTimer *timer = [[NSTimer alloc]initWithFireDate:[NSDate distantPast] interval:1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
这种方式要想定时器工作,必须要把定时器添加到RunLoop的上,而我们一般做的就是讲定时器添加到默认模式上的。
3、相信大家试过的都知道用上面两种方法创建就会出现上图所示的问题。但是用下面的方法创建的话就不会出现。GCD创建定时器:
dispatch_queue_t myQuest = dispatch_get_main_queue();
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, myQuest);//这里要将定时器设为全局,否则定时器将不会启动。
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 1.0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"%ld,--%@--",++self.page,[NSRunLoop currentRunLoop]);
});
dispatch_resume(self.timer);
GCD中的单位是纳秒NSEC_PER_SEC这个宏可以点进去看一下是1000000000,这个苹果已经为我们做好了不用管,就按照我们平时的那种做法多少秒就写几就好了。
实验发现,用NSTimer创建,要解决定时器失效的问题,要将定时器添加到UI模式下:UITrackingRunLoopMode和NSDefaultRunLoopMode下才可以,通常直接写NSRunLoopCommonModes(包含了UI和默认模式)才可以解决定时器失效的问题,而用GCD写的话就不用了,其实GCD也用只不过苹果帮我们封装好了,不用我们自己写了。
相信好多人不知道为什么回事这样的效果,原因是什么。
RunLoop运行循环,我们也可以叫做死循环,其实我们就可以把他当做是一个佣人(程序的佣人);
每一个程勋都有一个RunLoop,我们的操作都是由他来帮我们实现的RunLoop总共有五种模式,只不过我们用到的只有上面三种。
每一个模式下有souce,obsever,Timer三个服务(优先级从高到低);而我们的Timer就是在这里被执行的,平常的时候我们在默认模式下,RunLoop就会帮我们执行,但是有一些UI操作时,这时候RunLoop已经切换到UI模式了,因为UI模式的优先级比默认模式的优先级高,所以有一些滑动操作时,RunLoop就会去执行,因为每个应用程序只有一个RunLoop所以我们默认模式下的timer就不会工作了,所以我们想,RunLoop既然去为为UI模式工作了,那我们就把定时器放到UI模式下就可以了,事实证明真的如此。
有的人在想,那我们刚一开始就把timer直接放在UI模式下,这样做的话你会发现,跑起来的时候定时器也没起作用,只有在滑动界面的时候才会起作用。因为只有在做一些UI操作时才会触发RunLoop的UI模式,平常的时候是没有用的。所以我们一般就会把timer放在默认和UI模式下,这样不管正常还是UI滑动界面时都不会使定时器失效。可以看一下图更清晰:
有什么不对的地方请大家指正。或者谁有更好的理解。