「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战」
在日常开发中,经常会用到定时器,常用的定时器用以下几种:
- NSTimer
- CADisplayLink
- GCD定时器
NSTimer
关于 NSTimer
NSTimer :一个在确定时间间隔内执行一次或多次指定对象方法的对象
NSTimer 常用方法
-
需要手动将
timer加入runloop中,NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES]; //NSRunLoopCommonModes 包含了 NSDefaultRunLoopMode 和 UITrackingRunLoopMode(所以滑动的时候也能响应定时器) [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; -
默认会自动添加到
NSDefaultRunLoopModeNSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES]; -
timer的销毁invalidate方法 会停止计时器的再次触发,并在RunLoop中将其移除invalidate方法 是将timer对象从runloop中移除的唯一方法- 调用
invalidate方法会删除runloop对timer的强引用,以及timer对target和userInfo的强引用
[timer invalidate]; timer = nil;
NSTimer 在线程中的使用
-
在子线程上直接使用是没有反应的,因为
runloop在子线程上,需要手动把当前的runloop开启:[[NSRunLoop currentRunLoop] run]dispatch_queue_t queue = dispatch_get_global_queue(0,0); dispatch_async(queue, ^{ self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES]; //timer 加入runloop [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; //开启runloop [[NSRunLoop currentRunLoop] run]; }); -
在子线程上创建的定时器,必须要在子线程中销毁,不要在主线程中销毁,否者会造成内存泄漏
- (void)viewDidLoad { [super viewDidLoad]; dispatch_queue_t queue = dispatch_get_global_queue(0,0); dispatch_async(queue, ^{ self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; }); } static int i = 0; - (void)run{ if (i == 4) { //子线程销毁 [self.timer invalidate]; } i++; } -
runloop的创建方式不是通过alloc init是通过[NSRunLoop currentRunLoop]来直接获取 -
如果当前线程中有大量的复杂操作,会导致定时器的卡住 示例:
- (void)viewDidLoad { [super viewDidLoad]; dispatch_queue_t queue = dispatch_get_global_queue(0,0); dispatch_async(queue, ^{ self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; [[NSRunLoop currentRunLoop] run]; }); } - (void)run{ //线程中的耗时操作 NSUInteger count = 0xFFFFFFF; CGFloat num = 0; for (int i = 0; i < count; i ++) { num = i/count; } NSLog(@"%@",[NSThread currentThread]); }log:
NSTimer 在UI中的使用
使用类似 scrollView 或 tableView 这样的控件,滑动UI的时候,定时器会暂停,原因是默认的 Timer 模式是 NSDefaultRunLoopMode ,但是在滑动的时候 runloop 的模式是 UITrackingRunLoopMode。runloop 同一时刻只能在一个 mode 上来运行,其他 mode 上的任务暂停,所以在 Timer 中最好是设置 mode 的模式为 NSRunLoopCommonModes ,因为 NSRunLoopCommonModes 包含了 NSDefaultRunLoopMode 和 UITrackingRunLoopMode,所以滑动的时候也能响应定时器