在应用开发过程中,会遇到很多需要循环执行或者间隔执行任务的需求,这时就会遇到定时器的使用
NSTimer
- 不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoop和RunLoop Mode有关
- 如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会在延时结束后立刻执行,并按照之前指定的周期继续执行。
- 必须加入Runloop:使用scheduledTimerWithTimeInterval的创建方式,会自动把timer加入MainRunloop的NSDefaultRunLoopMode中。
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:) userInfo:nil repeats:NO];
- 如果使用以下方式创建定时器,就必须手动加入Runloop:
- NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode 解决滚动的时候计时问题
NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
- 存在的风险
- scheduledTimerWithTimeInterval方法将target设为A对象时,A对象会被这个timer所持有,也就是会被retain一次,timer会被当前的runloop所持有。
- 你创建的对象X中有一个实例变量_timer,方法fireTimer触发一个timer,方法cancel取消这个timer。在对象X需要销毁的时候,需要将它的timer取消掉
- 不幸的是,dealloc方法将永远不会被调用。因为timer的引用,对象X的引用计数永远不会降到0,dealloc方法也就不会被调用。这时如果不调用cancel,对象X将永远无法释放,造成内存泄露。
- (void)fireTimer {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(action:)
userInfo:nil
repeats:YES];
}
- (void)cancel {
[_timer invalidate];
}
- (void)dealloc {
[_timer invalidate];
}
CADisplayLink
- CADisplayLink 是一个定时器对象可以让你的应用以与显示器的刷新界面相同的频率进行绘图。
- 应用如果想要创建 display link ,需要提供一个目标对象和一个当屏幕刷新时被调用的选择器方法。之后再将 display link 添加到主线程中
- 在动画中最好用CADisplayLink,因为由于每秒的刷新频率较高,所以用它所生成的动画会显得非常流畅。
- CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒
- 延迟:iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。
- 但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
GCD
GCD中的定时器:
-
1、GCD中的定时器不受NSRunLoop影响
-
2、必须有强引用,引用该timer,要不,在方法执行完毕后timer就会被销毁,所以就不能执行定时器的任务,所以必须设置强引用timer,来保住timer的命来执行任务(因为其timer的定时器任务,不会立即执行,会间隔一段时间执行)
-
执行一次
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//执行事件
});
- 重复执行
NSTimeInterval period = 1.0; //设置时间间隔
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
//在这里执行事件
});
dispatch_resume(_timer);