美文网首页
内存管理 之 gcd定时器

内存管理 之 gcd定时器

作者: ychen3022 | 来源:发表于2019-01-06 16:44 被阅读8次
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,不准时
这两种方法缺点都有对应的方法去解决。

相关文章

网友评论

      本文标题:内存管理 之 gcd定时器

      本文链接:https://www.haomeiwen.com/subject/jddhrqtx.html