什么是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,会在本次调用完毕之后invalidate掉NSTimer自身,而NSTimer做retain的对象也会被进行一次release。但是如果是多次重复调用的NSTimer,就需要我们自己在某个特定的时刻来invalidate掉NSTimer,这个invalidate的时刻是根据我们代码情况来自己决定的,否则将会一直存在。
下面的方法我们先创建了一个Object对象,然后添加了一个timer(关于NSTimer和runloop后面再讲),并且进行了一次release,这时object并没有被释放,而是被NSTimer进行了一次retain,我们通过在Object的dealloc方法中打印就可以知道是否被释放。
在本次NSTimer的timer所调用方法调用完毕之后,NSTimer会invalidate自身,而object对象也会被释放。
Object *object = [[Object alloc] init];
[NSTimer scheduledTimerWithTimeInterval:5 target:object selector:@selector(timerAction:) userInfo:nil repeats:NO];
[object release];
而通过下面这种方式创建的timer就不会被NSTimer自动释放,因为这次调用是重复调用,必须我们显示的进行invalidate,NSTimer才会消失,这时obect对象也就会释放了。
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会默认为我们添加到Runloop的default mode中,而且由于是在主线程中,所以Runloop是开启的,不需要我们手动打开。
在我们进行多线程编程时,所有的source都需要添加到Runloop中才能生效,对于我们的NSTimer当然也需要添加到Runloop中才能生效。如果一个Runloop中没有任何source的话,会立即退出的。而主线程的Runloop在程序运行时,系统就已经为我们添加了很多source到Runloop中,所以主线程的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中,最后一定不要忘记调用runloop的run方法将当前runloop开启,否则NSTimer永远也不会运行。
除了上面的问题,还有一个问题就是Mode选择不正确,这种情况比较少见,可以参考以下博客:
http://www.mgenware.com/blog/?p=459