timer的延时和优化

了解更多IOS底层原理知识,关注腾讯课堂八点钟学院IOS高级开发 

 IOS学习交流群 431449751;


     NSTimer从官方给出的解释可以看出timer会在未来的某个时刻执行一次或者多次我们指定的方法,这也就牵扯出一个问题,如何保证timer在未来的某个时刻触发指定事件的时候,我们指定的方法是有效的呢?

  解决方法很简单,只要将指定给timer的方法的接收者retain一份就搞定了,实际上系统也是这样做的。不管是重复性的timer还是一次性的timer都会对它的方法的接收者进行retain,这两种timer的区别在于“一次性的timer在完成调用以后会自动将自己invalidate,而重复的timer则将永生,直到你显示的invalidate它为止”。

NSTimer会是准时触发事件吗

  答案是否定的,而且有时候你会发现实际的触发时间跟你想象的差距还比较大。NSTimer不是一个实时系统,因此不管是一次性的还是周期性的,timer的实际触发事件的时间可能都会跟我们预想的会有出入。差距的大小跟当前我们程序的执行情况有关系,比如可能程序是多线程的,而你的timer只是添加在某一个线程的runloop的某一种指定的runloopmode中,由于多线程通常都是分时执行的,而且每次执行的mode也可能随着实际情况发生变化。

  假设你添加了一个timer指定2秒后触发某一个事件,但是签好那个时候当前线程在执行一个连续运算(例如大数据块的处理等),这个时候timer就会延迟到该连续运算执行完以后才会执行。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会和后面的触发进行合并,即在一个周期内只会触发一次。但是不管该timer的触发时间延迟的有多离谱,他后面的timer的触发时间总是倍数于第一次添加timer的间隙。

NSTimer为什么要添加到RunLoop中才会有作用

  我们使用的是一种便利方法,它其实是做了两件事:首先创建一个timer,然后将该timer添加到当前runloop的default mode中。也就是这个便利方法给我们造成了只要创建了timer就可以生效的错觉,我们当然可以自己创建timer,然后手动的把它添加到指定runloop的指定mode中去。

  NSTimer其实也是一种资源,如果看过多线程编程指引文档的话,我们会发现所有的source如果要起作用,就得加到runloop中去。同理timer这种资源要想起作用,那肯定也需要加到runloop中才会生效喽。如果一个runloop里面不包含任何资源的话,运行该runloop时会立马退出。你可能会说那我们APP的主线程的runloop我们没有往其中添加任何资源,为什么它还好好的运行。我们不添加,不代表框架没有添加,如果有兴趣的话你可以打印一下main thread的runloop,你会发现有很多资源。 

NSTimer加到了RunLoop中但迟迟的不触发事件

  为什么明明添加了,但是就是不按照预先的逻辑触发事件呢???原因主要有以下两个:

1、runloop是否运行

  每一个线程都有它自己的runloop,程序的主线程会自动的使runloop生效,但对于我们自己新建的线程,它的runloop是不会自己运行起来,当我们需要使用它的runloop时,就得自己启动。

  那么如果我们把一个timer添加到了非主线的runloop中,它还会按照预期按时触发吗?下面请看一段测试程序:

[cpp]  view plain copy
  1. //  
  2. //  ViewController.m  
  3. //  NSTimerDemo  
  4. //  
  5. //  Created by administrator on 13-6-24.  
  6. //  Copyright (c) 2013年 enuola. All rights reserved.  
  7. //  
  8.   
  9. #import "ViewController.h"  
  10. #import "TestObject.h"  
  11.   
  12. @interface ViewController ()  
  13.   
  14. @end  
  15.   
  16. @implementation ViewController  
  17.   
  18. - (void)viewDidLoad  
  19. {  
  20.     [super viewDidLoad];  
  21.     // Do any additional setup after loading the view, typically from a nib.  
  22.       
  23.     //另开一个新的线程,进行测试,而非主线程。  
  24.     [NSThread detachNewThreadSelector:@selector(testTimerSheduleToRunloop1) toTarget:self withObject:nil];  
  25. }  
  26.   
  27. //测试把timer加到不运行的runloop上的情况  
  28. -(void)testTimerSheduleToRunloop1  
  29. {  
  30.     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  31.       
  32.     NSLog(@"Test timer shedult to a non-running runloop");  
  33.       
  34.     TestObject *testObject4 = [[TestObject alloc] init];  
  35.       
  36.     NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:testObject4 selector:@selector(timerAction:) userInfo:nil repeats:NO];  
  37.       
  38.     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];  
  39.     //打开下面一行输出runloop的内容就可以看出,timer确实已经被添加进去  
  40. //    NSLog(@"the thread's runloop: %@", [NSRunLoop currentRunLoop]);  
  41.       
  42.     //打开下面一行,该线程的runloop就会运行起来,timer才会起作用  
  43. //    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];  
  44.       
  45.     [testObject4 release];  
  46.     NSLog(@"invoke release to testObject4");  
  47.       
  48.     [pool release];  
  49.       
  50. }  
  51.   
  52. - (void)didReceiveMemoryWarning  
  53. {  
  54.     [super didReceiveMemoryWarning];  
  55.     // Dispose of any resources that can be recreated.  
  56. }  
  57.   
  58. @end  

上面的程序中,我们新创建了一个线程,然后创建一个timer,并把它添加当该线程的runloop当中,但是运行结果如下:

可以看到,线程在执行到timer时,在等待timer的执行,再去执行timer之后的东西,阻止线程结束。

其中,第一行注释的代码,可以打印输出该线程的Runloop中确实已经有了timer资源。观察运行结果,我们发现这个timer知道执行退出也没有触发我们指定的方法,如果我们把上面测试程序中:

[cpp]  view plain copy
  1. //    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];  

这一行的注释去掉,则timer将会正确的调用我们指定的方法。

2、mode是否正确

  我们前面自己动手添加runloop的时候,可以看到有一个参数runloopMode,这个参数是干嘛的呢?

  前面提到了要想timer生效,我们就得把它添加到指定runloop的指定mode中去,通常是主线程的defalut mode。但有时我们这样做了,却仍然发现timer还是没有触发事件。这是为什么呢?

  这是因为timer添加的时候,我们需要指定一个mode,因为同一线程的runloop在运行的时候,任意时刻只能处于一种mode。所以只能当程序处于这种mode的时候,timer才能得到触发事件的机会。

  举个不恰当的例子,我们说兄弟几个分别代表runloop的mode,timer代表他们自己的水桶,然后一群人去排队打水,只有一个水龙头,那么同一时刻,肯定只能有一个人处于接水的状态。也就是说你虽然给了老二一个桶,但是还没轮到它,那么你就得等,只有轮到他的时候你的水桶才能派上用场。

  综上: 要让timer生效,必须保证该线程的runloop已启动,而且其运行的runloop mode也要匹配。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值