用法
NSTimer的用法很简单,个人最常用的是下面这个方法。
[NSTimer scheduledTimerWithTimeInterval:0.5f
target:self
selector:@selector(test)
userInfo:nil
repeats:YES];
问题:为何停止
有一些人会遇到在页面滑动的时候会发现定时器没有执行
看上面代码你会发现并没有把NSTimer添加到NSRunLoop里面,这种情况会默认把NSTimer添加到NSRunLoop上并且设置mode为NSDefaultRunLoopMode。
在页面滑动的时候,NSRunLoop会自动把Mode切换到 UITrackingRunLoopMode,而默认的却是在NSDefaultRunLoopMode上 ,所以这时候的NSTimer是不会执行的。
解决这个问题挺简单,就是我们要把NSTimer添加到NSRunLoop下的NSRunLoopCommonModes模式下,代码如下:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5f
target:self
selector:@selector(test)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
这样就会解决NSTimer滚动时的停止问题。
问题:是否精确
首先告诉你答案:不精确
NSTimer不精确的原因有很多种:
其一 就是类似于上边这种情况,没有把NSTimer添加到RunLoop里面,会默认设置模式为NSDefaultRunLoopMode,在页面滚动到时候会改变RunLoop的Mode为UITrackingRunLoopMode,这个时候肯定是不准确的。
其二 NSTimer是依赖于RunLoop来执行的,如果当前线程里面有太多的逻辑处理、或者数据运算,会影响到RunLoop,所以当前线程如果有非常多的事要处理的话NSTimer的精确性会差一点。
解决办法:既然说了是因为当前线程处理非常的的事,会造成NSTimer丢失准确性,那么你就尽可能的让NSTimer所在的线程尽可能的空闲,比如你可以开一个子线程:
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.5f
target:self
selector:@selector(test)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
});
但是现在你想一下,就算NSTimer所在的线程完全空闲,真的就能使得NSTimer完全准确吗?答案是不能。只能做到相对你能达到的最准确的Timer了。造成的原因是因为苹果有设置一个最小容差的属性tolerance,默认是 0 但是就算你设置为0,苹果依然会有权限有一个最小的容差,因为可以优化电量。
所以你只能做到相对精确,但是做不到完全精确。
停止Timer
NSTimer提供了一个- (void)invalidate方法,用来结束NSTimer的。在这里需要注意一点:NSTimer的开始跟结束必需要放在一个线程里面,如果A线程开始,B线程结束,你猜NSTimer会��结束吗?
还有一种方法就是:
[timer setFireDate:[NSDate distantFuture]];
timer = nil;
先把timer设置触发的时间设置为future(未来,四级都过了吧?),这样就永远不会触发这个定时器,然后nil。(perfect)
其实第二种方法是对应的暂停跟开始Timer:
//开始
[timer setFireDate:[NSDate distantPast]];
//暂停
[timer setFireDate:[NSDate distantFuture]];
以上是个人平时遇到过的问题以及解决办法,如有错误欢迎指正。