简述:一个在确定时间间隔内执行一次或者多次我们指定对象方法的对象。
如果是多次执行对象方法,那么它怎么保证执行的时候对象不被释放呢?
问题:
1.你知道NSTimer会retain你添加调用方法的对象吗?
2.你知道NSTimer并不是每次都准确按你设定的时间间隔来触发吗?
3.NSTimer需要和NSRunLoop结合起来使用,你知道是怎么结合使用的吗?
4.你知道除了用NSTimer实现定时器,还有别的方式也能实现定时器吗?
大纲:
1.定时器的应用场景
2.定时器的常用方式
3.fire方法的正确理解
4.NSRunLoopMode对定时器的影响
5.定时器一定会在规定时间间隔内实时出发事件吗?
6.定时器引起的循环引用
7.子线程开启定时器
8.GCD定时器
一:定时器的使用
第一种:
_timer = [NSTimer timerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];//需要手动把定时器加入到RunLoop里面才会生效
第二种:
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES]; //系统会自动加到RunLoop里面
注:iOS10之后增加了block方式的定时器初始化,为了避免之前方式所引起的循环引用问题:timer和scheduled都有:
(这样就会消除循环引用,正常调用dealloc方法了)
__weak typeof(self) weakSelf = self;
_timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
// NSLog(@"blockAction");
[weakSelf timerAction];
}];
实时触发定时器,调用定时器方法,而且不会影响timer的响应周期
[_timer fire];
定时器销毁的唯一方法:
[_timer invalidate];
注意点1:子线程上的定时器,请在子线程销毁
[NSThread detachNewThreadSelector:@selector(ThreadTimer) toTarget:self withObject:nil];
-
(void)ThreadTimer {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];//子线程上的定时器,必须要在子线程里面invalidate,否则会造成资源的浪费
[self performSelector:@selector(invalidateTimer) withObject:nil afterDelay:3.f];
//RunLoop在子线程上,是需要你手动开启,RunLoop
//RunLoop 的创建 [NSRunLoop currentRunLoop],当获取当前RunLoop的时候,系统就已经自动创建好了,并不是alloc
[[NSRunLoop currentRunLoop] run];
NSLog(@"我在学习中");//当RunLoop结束 才会执行下面的语句
}
注意点2:注意定时器的线程里,不要做复杂的计算操作,这样会导致定时器不准确
GCD定时器:
//内存问题:当timeTwo(且eventhandle里没有包含timer) 为成员变量的时候 没问题;当为局部变量的时候,只运行一次
__weak typeof(self) weakSelf = self;
dispatch_queue_t queue = dispatch_queue_create("gcd实现定时器", 0);
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.f * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
// timer;//引用计数,不会被释放
// if (!repeats) {
// dispatch_cancel(timer);
// }else{
[weakSelf timerAction];
// }
});
dispatch_resume(timer);
gcd定时器的销毁:dispatch_cancel(timer);
Display定时器:
创建:
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(timerAction)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
停止:
[displayLink invalidate];
①屏幕刷新时调用
CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒
②延迟
iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。
如果CPU过于繁忙,无法保证屏幕60次/秒的刷新率,就会导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。
③使用场景
从原理上可以看出,CADisplayLink适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。