NSTimer和Runloop的关系

作者: 刘小壮 | 来源:发表于2015-07-05 16:08 被阅读6960次
    该文章属于<简书 — 刘小壮>原创,转载请注明:

    <简书 — 刘小壮> http://www.jianshu.com/p/f9999b5958f8


    前段时间一直在找工作,现在刚刚到新公司入职,算是稳定下来了。新公司是国内某家做地图导航的公司,技术团队比较强大,我对这份工作还是非常满意的,希望到新公司可以学到更多的新知识,学海无涯,努力吧!

    这段时间找工作很忙,也没有写博客,现在把我这段时间积累的一些知识总结到博客中,大家一起分享。


    占位图
    什么是NSTimer ?

    “A timer provides a way to perform a delayed action or a periodic action. The timer waits until a certain time interval has elapsed and then fires, sending a specified message to a specified object. ”

    翻译过来就是Timer就是一个能在从现在开始的后面的某一个时刻或者周期性的执行我们指定的方法的对象。

    NSTimer怎样保证参数的生命周期

    NSTimer可以选择是否重复执行,为了保证NSTimer调用的方法中传递的对象生命周期,NSTimer会对外界传递的对象进行一次retain

    如果是一次性调用的NSTimer,会在本次调用完毕之后invalidateNSTimer自身,而NSTimerretain的对象也会被进行一次release。但是如果是多次重复调用的NSTimer,就需要我们自己在某个特定的时刻来invalidateNSTimer,这个invalidate的时刻是根据我们代码情况来自己决定的,否则将会一直存在。

    下面的方法我们先创建了一个Object对象,然后添加了一个NSTimer(关于NSTimerRunloop后面再讲),并且进行了一次release,这时Object并没有被释放,而是被NSTimer进行了一次retain,我们通过在Objectdealloc方法中打印就可以知道是否被释放。

    在本次NSTimerTimer所调用方法调用完毕之后,NSTimerinvalidate自身,而Object对象也会被释放。

      Object *object = [[Object alloc] init];  
      [NSTimer scheduledTimerWithTimeInterval:5 target:object selector:@selector(timerAction:) userInfo:nil repeats:NO];  
      [object release]; 
    

    而通过下面这种方式创建的Timer就不会被NSTimer自动释放,因为这次调用是重复调用,必须我们显示的进行invalidateNSTimer才会消失,这时Object对象也就会释放了。

      Object *object = [[Object alloc] init];  
      [NSTimer scheduledTimerWithTimeInterval:5 target:object selector:@selector(timerAction:) userInfo:nil repeats:YES];  
      [object release]; 
    

    总结:
    如果使用重复的NSTimer一定要有对应的invalidate,否则Timer会一直存在。
    NSTimer会对target对象进行一次retain,所以我们要注意target对象的生命周期。

    NSTimer的实时性

    无论是单次执行的NSTimer还是重复执行的NSTimer都不是准时的,这与当前NSTimer所处的线程有很大的关系,如果NSTimer当前所处的线程正在进行大数据处理(假设为一个大循环),NSTimer本次执行会等到这个大数据处理完毕之后才会继续执行。

    这期间有可能会错过很多次NSTimer的循环周期,但是NSTimer并不会将前面错过的执行次数在后面都执行一遍,而是继续执行后面的循环,也就是在一个循环周期内只会执行一次循环。

    无论循环延迟的多离谱,循环间隔都不会发生变化,在进行完大数据处理之后,有可能会立即执行一次NSTimer循环,但是后面的循环间隔始终和第一次添加循环时的间隔相同。

    NSTimer与Runloop的关系

    我们前面做演示的代码创建的NSTimer会默认为我们添加到RunloopNSDefaultRunLoopMode中,而且由于是在主线程中,所以Runloop是开启的,不需要我们手动打开。

    在我们进行多线程编程时,所有的Source都需要添加到Runloop中才能生效,对于我们的NSTimer当然也需要添加到Runloop中才能生效。如果一个Runloop中没有任何Source的话,会立即退出的。而主线程的Runloop在程序运行时,系统就已经为我们添加了很多SourceRunloop中,所以主线程的Runloop是一直存在的,我们可以通过打印MainThread中的Runloop来查看所包含的Source

    下面的代码就没有添加到Runloop中,所以这个NSTimer永远也不会发生作用,这是一份错误的代码示例。

      Object *object = [[Object alloc] init];  
      NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:object selector:@selector(timerAction:) userInfo:nil repeats:NO];  
      [object release];  
    
    NSTimer添加到Runloop中,但是不运行

    iOS多线程中,每一个线程都有一个Runloop,但是只有主线程的Runloop默认是打开的,其他子线程也就是我们创建的线程的Runloop默认是关闭的,需要我们手动运行。

    我们可以通过[NSRunLoop currentRunLoop]来获得当前线程的Runloop,并且调用[runloop addTimer:timer forMode:NSDefaultRunLoopMode]方法将定时器添加到Runloop中,最后一定不要忘记调用Runlooprun方法将当前Runloop开启,否则NSTimer永远也不会运行。

    除了上面的问题,还有一个问题就是Mode选择不正确,这种情况比较少见,可以参考以下博客:
    http://www.mgenware.com/blog/?p=459

    相关文章

      网友评论

      • helinyu:问一下,怎么写出下面的目录的?
        刘小壮:简书自带的
      • Sniper_Zheng:nstimer 真的不会在后面把前面错过的执行次数执行一遍么? 很多的话我不知道 即使一两次也不会?
        刘小壮:@Sniper_Zheng 我说的也是大数据处理完执行的那次:flushed:
        Sniper_Zheng:重新看了下,是我看漏了. 我说的是大数据处理完立即执行那次.... 感谢楼主分享~
        刘小壮:就像文中写的,在延迟后会立即执行一次timer,其他错过的就错过了。
      • 一叶__知秋:讲得很好很详细
      • 小蜗牛吱呀之悠悠:感谢楼主,感谢老师,向楼主学习
        刘小壮:您太客气啦
      • 一骑红尘妃子笑____:想请教一下,假如我们修改了runLoop的mode,把NSDefaultRunLoopMode该为NSRunLoopCommonModes是不是就可保证NSTimer的实时性?那么这么多会不会有其他方面的隐患?
        刘小壮:@一骑红尘妃子笑____ 暂时没有这方面的打算。这段时间在写CoreData系列的文章,大概下星期就出来了。
        一骑红尘妃子笑____:嗯,知道了,非常感谢。一直关注你的博客,看你也在做地图导航方面的东西,最近有没有计划写一写地图sdk的使用心得或者优化方面的文章。我也是做这方面的
        刘小壮:@一骑红尘妃子笑____ 可以保证NSTimer一直被执行。如果NSTimer回调方法中如果执行耗时逻辑,可能会导致主线程卡顿,当时苹果这样的设计,也是为了优化滑动流畅性。
      • 9e233c805709:@刘小壮 谢谢讲解,之前没考虑在子线程的情况。
        刘小壮:@汤奇V 没事的,早点休息 :blush:
      • 9e233c805709:请问文中最后「最后一定不要忘记调用Runloop的run方法将当前Runloop开启,否则NSTimer永远也不会运行。」这句话如何理解呢?
        刘小壮:@汤奇V 如果在一个子线程中添加一个NSTimer,子线程的Runloop默认是关闭的,需要我们主动将子线程的Runloop开启,否则添加到子线程的NSTimer永远不会执行。

      本文标题:NSTimer和Runloop的关系

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