1、NSTimer并不准时
NSTimer是我们经常使用到的计时器,那么它是否符合严格意义上的准时呢?答案是否定的。
我们可以通过事例来证实这种现象:可以看出时间间隔都已经超过一秒了
- (void)viewDidLoad {
[super viewDidLoad];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
}
-(void)run{
int i=0;
while (i < 1000000000) {
i++;
}
NSLog(@"Test");
}
打印结果:
============================================
2019-01-04 15:50:45.259613+0800 Test[5566:176055] Test
2019-01-04 15:50:47.196497+0800 Test[5566:176055] Test
2019-01-04 15:50:49.290460+0800 Test[5566:176055] Test
2019-01-04 15:50:51.288239+0800 Test[5566:176055] Test
2019-01-04 15:50:53.310628+0800 Test[5566:176055] Test
-
NSTimer不准时原因
其实这种现象产生的原因就是因为NSTimer依赖于RunLoop,如果RunLoop的任务过于繁重(假设当前线程正在进行大数据处理 ,需要很长时间),NSTimer本次执行会等到这个大数据处理完毕之后才会继续执行,这样就可能导致NSTimer不准时。 -
解决方法(主要是针对该线程有耗时任务执行情况)
1、在子线程中创建timer,在主线程进行定时任务的操作
2、在子线程中创建timer,在子线程中进行定时任务的操作,需要UI操作时切换回主线程进行操作 -
NSTimer与RunLoop的关系
//情况0:无需添加timer至runloop中,默认添加到主线程的runloop中,且模式为NSDefaultRunLoopMode
//如果在子线程中定义的话,不会开启,因为子线程中的runloop需要手动开启
NSTimer *timer0 = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//情况1:添加至runloop中,且设置为NSDefaultRunLoopMode
//但是这种模式下如果拖拽界面,runloop会自动进入UITrackingRunLoopMode,优先于定时器追踪模式,所以定时器会暂停,停止拖拽页面后,定时器恢复
NSTimer *timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer1 forMode:NSDefaultRunLoopMode];
//情况2:添加至runloop中,且设置为NSRunLoopCommonModes
//无论是界面追踪还是普通模式都可以运行,NSRunLoopCommonModes为一个伪模式,其本质是NSDefaultRunLoopMode和UITrackingRunLoopMode的合集
NSTimer *timer2 = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer2 forMode:NSRunLoopCommonModes];
2、gcd定时器
gcd定时器的使用
#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) dispatch_source_t timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//队列
dispatch_queue_t queue = dispatch_get_main_queue();
//创建定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//设置时间
uint64_t start = 2.0;//2秒后开始执行
uint64_t interval = 1.0;//每隔1秒执行
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);
//设置回调
dispatch_source_set_event_handler(timer, ^{
NSLog(@"定时器执行任务");
});
//启动定时器
dispatch_resume(timer);
self.timer = timer;
}
@end
不过gcd定时器的使用过程中,代码有些散乱,大家可以自行封装使用。
3、总结
结合上一篇文章所说,使用NSTimer和CADisplayLink的缺点有两个:
<1>、会产生强引用
<2>、基于runtime,不准时
这两种方法缺点都有对应的方法去解决。
网友评论