iOS Developer

NSTimer在使用中需要注意的点

2017-12-07  本文已影响349人  SnoopPanda
NSTimer的常用API
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
  Initializes a timer object with the specified object and selector.
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
  Creates a timer and schedules it on the current run loop in the default mode.

根据官方文档方法一和方法二的区别在于:使用方法一创建的Timer不会添加到NSRunLoop需要手动添加;使用方法二会自动添加到主线程的RunLoop中。

- (void)fire; 
  Causes the timer's message to be sent to its target.
- (void)invalidate;
  Stops the timer from ever firing again and requests its removal from its run loop.

fire方法可以立即触发Timer对象的target方法;invalidate会停止Timer并将其从runloop中移除

NSRunLoop & NSTimer

当使用NSTimerscheduledTimerWithTimeInterval方法时,NSTimer的实例会被加入到当前线程的RunLoop中,模式为默认模式NSDefaultRunLoopMode

- (void)viewDidLoad
{
    [super viewDidLoad];
        
    NSLog(@"主线程 %@", [NSThread currentThread]);
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(doSomething) userInfo:nil repeats:YES];
    //使用NSRunLoopCommonModes模式,把timer加入到当前Run Loop中。
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

- (void)doSomething
{
    NSLog(@"Timer %@", [NSThread currentThread]);
}

控制台输出结果:上下打印出来的线程都是主线程。

如果当你线程是主线程也就是UI线程时,某些UI事件(UIScrollView的滑动操作),会将RunLoop切换到NSEventTrackingRunLoopMode模式。
在这个过程中,默认的NSDefaultRunLoopMode模式中注册的事件是不会被执行的。也就是说,此时使用scheduledTimerWithTimeInterval添加到RunLoop中的Timer就不会执行。这是一个在开发中经常碰到的场景,如何解决这个问题?我们可以在创建完一个Timer后使用NSRunLoopaddTimer:forMode:方法,使用NSRunLoopCommonModes。这是一个占位用的Mode,可以在不同情况下扮演不同的角色——NSDefaultRunLoopMode & UITrackingRunLoopMode

NSTimer循环引用问题

NSTimer为什么会造成循环引用?
在开发中我们经常将Timer作为控制器的属性来使用,这样一来控制器对Timer进行了强引用。在target-action这个过程中,Timer又对self做了强引用,这就是导致循环引用的原因了。在网上有非常多的解决方案,我总结了一下有下面几种。

The timer maintains a strong reference to this object until it (the timer) is invalidated.苹果文档说invalidate以后timer就不再保有对target的强引用了。所以解决循环引用的关键在与invalidate方法有没有执行。下面_timer = nil这句话的意义是,invalidate方法执行以后Timer就不能复用了,为了防止在其之后其他地方再次使用Timer,在这里即使将其置为nil。

上一篇 下一篇

猜你喜欢

热点阅读