1.NSTimer的创建
- 常用方法一:该方法内部自动添加到runloop中, 运行模式为默认
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
// 如果在子线程中调用改方法,不会执行,因为子线程runloop必须手动开启:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(repeat:) userInfo:@{@"key":@"value"} repeats:true];
// 子线程runloop手动开启
[[NSRunLoop currentRunLoop] run];
});
- 不常用方法二:该方法创建的NSTimer必须手动添加到runloop中
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[runloop addTimer:timer forMode:NSDefaultRunLoopMode];
- 不常用方法三:两个方法创建的NSTimer必须手动添加到runloop中
- (instancetype)initWithFireDate: interval: target: selector: userInfo: repeats:
- (void)initWithFireDate: interval: repeats: block:
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
2.NSTimer与Runloop:
- 为什么NSTimer要添加到Runloop:
- NSTimer是一种source,所以的source要起作用,都要添加到runloop中
- 如果添加到了kCFRunLoopDefaultMode模式中,在拖动界面的时候,runloop模式只能唯一,这时切换为UITrackingRunLoopMode,定时器停止工作
- 如果添加到了kCFRunLoopCommonModes模式中,任何runloop模式都可以工作了
- Runloop的三种模式:
// 默认模式
kCFRunLoopDefaultMode
// 拖动模式
UITrackingRunLoopMode
// 此模式包含了以上两种模式
kCFRunLoopCommonModes
3.NSTimer的循环引用
- 创建NSTimer时,最后一个参数如果为YES,那么NSTimer对于target:self形成强引用;而self因为后续要用到定时器,所以对于定时器也形成了强引用;这就形成了循环引用
- 如果我们启动了一个NSTimer,在某个界面释放前,将这个NSTimer停止,甚至置为nil,都不能使这个界面释放,原因是系统的循环池中还保有这个对象
4.如何解决循环引用
4.1.控制器不再引用NSTimer
4.2.NSTimer不再引用控制器
- fire方法
- 启动 Timer, 触发Target的方法调用但是并不会改变Timer的时间设置。 如果定时器的方法是non-repeating,调用之后自动销毁
- (void)fire;
- 销毁NSTimer的唯一办法:(nstimer的创建和销毁要在一个runloop中)
// 要在dealloc方法调用之前销毁NSTimer
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (timer.isValid) {
// 从runloop中销毁,销毁系统对于NSTimer的强引用
[_timer invalidate];
// 将属性置空销毁,销毁self对于NSTimer的强引用
_timer = nil;
}
}
.5.GCD定时器
- GCD 绝对精准,不受runloop影响
#import "ViewController.h"
@interface ViewController ()
/** 定时器属性 */
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1.创建GCD中的定时器
/*
第一个参数: source的类型DISPATCH_SOURCE_TYPE_TIMER 表示是定时器
第二个参数: 描述信息,线程ID
第三个参数: 更详细的描述信息
第四个参数: 队列,决定GCD定时器中的任务在哪个线程中执行
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//2.设置定时器(起始时间|间隔时间|精准度)
/*
第一个参数:定时器对象
第二个参数:起始时间,DISPATCH_TIME_NOW 从现在开始计时
第三个参数:间隔时间 2.0 GCD中时间单位为纳秒
第四个参数:精准度 绝对精准0
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3.设置定时器执行的任务
dispatch_source_set_event_handler(timer, ^{
NSLog(@"GCD---%@",[NSThread currentThread]);
});
//4.启动执行
dispatch_resume(timer);
self.timer = timer;
}
@end
6.CADisplayLink定时器
- 创建CADisplayLink定时器
self.displayLink = [CADisplayLink displayLinkWithTarget:self sel:ector:@selector(handleDisplaylink:)];
[self.displayLink addToRunLoop:[NSRunLoop curentRunLoop] forMode:NSDefaultRunLoopMode];
- 停止CADisplayLink定时器
[self.displayLink invailidate];
self.displayLink = nil;
-
把CADisplayLink添加到runloop中后,方法就会被周期性调用。invailidate后,CADisplayLink从runloop中移除,方法停止调用
-
使用场景:
- 以屏幕的刷新频率执行某操作(刷新频率通常为60次/秒)
- 界面重绘,比如视频播放
-
CADisplayLink延迟问题
- 如果执行的操作比较费时,会出现跳过执行操作的情况
网友评论