#####该文章属于<简书 — 刘小壮>原创,转载请注明:
<简书 — 刘小壮> 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
,会在本次调用完毕之后invalidate
掉NSTimer
自身,而NSTimer
做retain
的对象也会被进行一次release
。但是如果是多次重复调用的NSTimer
,就需要我们自己在某个特定的时刻来invalidate
掉NSTimer
,这个invalidate
的时刻是根据我们代码情况来自己决定的,否则将会一直存在。
下面的方法我们先创建了一个Object
对象,然后添加了一个NSTimer
(关于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
才会消失,这时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
会默认为我们添加到Runloop
的NSDefaultRunLoopMode
中,而且由于是在主线程中,所以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