NSTimer (IOS开发)

本文介绍了iOS开发中的NSTimer,详细讲解了其基本概念、使用方法、触发、停止以及注意事项。NSTimer是一个定时执行指定方法的对象,可以通过多种初始化方法创建,包括block和selector形式。使用时需要注意将其添加到Runloop中,并关注target的生命周期,因为NSTimer会对target进行强引用。此外,NSTimer的触发并不精确,可能存在延迟和跳过的情况,还涉及到时间宽容度(Tolerance)属性。确保将timer添加到合适的RunLoop模式,才能保证其正常工作。

一  什么是NSTimer

timer是一个能在从现在开始的后面的某一个时刻或者周期性的执行我们指定的方法的对象。总结为三要素:时间间隔、被触发、发送消息(执行方法)

 

二  NSTimer使用方法

  1. 初始化

系统提供了8个创建方法,6个类创建方法,2个实例初始化方法。

有三个方法直接将timer添加到了当前runloop default mode,而不需要我们自己操作,当然这样的代价是runloop只能是当前runloop,模式是default mode:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

下面五种创建,不会自动添加到runloop,还需调用addTimer: forMode:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep;

- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

//后五种方式下,添加以下语句,将timer添加到runloop

- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;

 

对上面所有方法参数做个说明:

  1. ti(interval)定时器触发间隔时间,单位为秒,可以是小数。如果值小于等于0.0的话,系统会默认赋值0.1毫秒 
  2. invocation这种形式用的比较少,大部分都是block和aSelector的形式 
  3. yesOrNo(rep)是否重复,如果是YES则重复触发,直到调用invalidate方法;如果是NO,则只触发一次就自动调用invalidate方法
  4. aTarget(t)发送消息的目标,timer会强引用aTarget,直到调用invalidate方法 
  5. aSelector(s)将要发送给aTarget的消息,如果带有参数则应:- (void)timerFireMethod:(NSTimer *)timer声明
  6. userInfo(ui)传递的用户信息。使用的话,首先aSelector须带有参数的声明,然后可以通过[timer userInfo]获取,也可以为nil,那么[timer userInfo]就为空
  7. date触发的时间,一般情况下我们都写[NSDate date],这样的话定时器会立马触发一次,并且以此时间为基准。如果没有此参数的方法,则都是以当前时间为基准,第一次触发时间是当前时间加上时间间隔ti
  8. blocktimer触发的时候会执行这个操作,带有一个参数,无返回值

(注:添加到runloop,参数timer是不能为空的,否则抛出异常)

(注: scheduled的初始化方法将以默认mode直接添加到当前的runloop中.不用scheduled方式初始化的,需要手动addTimer: forMode: 将timer添加到一个runloop中。)

 

      2 触发

当定时器创建完(不用scheduled的,添加到runloop中后,该定时器将在初始化时指定的timeInterval秒后自动触发。

     系统提供了一个- (void)fire;方法,调用它可以触发一次:

  1)在重复执行的定时器中调用此方法后立即触发该定时器,但不会中断其之前的执行计划;

  2)在不重复执行的定时器中调用此方法,立即触发后,就会使这个定时器失效。

 

    3 停止

- (void)invalidate;  这个是唯一一个可以将计时器从runloop中移出的方法。

  • timer不用了,一定要调用invalidate 
  • 一般是target释放的同时,才会知道timer不用了,那么怎么捕获target被释放了呢?dealloc方法肯定是不行的。如果是控制器的话可以尝试监听pop方法的调用(nav的代理),viewDidDisappear方法里面(但要记着,再次展示的时候从新添加。)

 

  • 注意事项
  1. timer会对它的target进行retain,我们需要小心对待这个target的生命周期问题,尤其是重复性的timer。
  2. timer不是一种实时的机制,会存在延迟,而且延迟的程度跟当前线程的执行情况有关。

         第一种不准时:有可能跳过去

        a 线程处理比耗时的事情时会发生 

        b 还有就是timer添加到的runloop模式不是runloop当前运行的模式,这种情况经常发生。

        c 虽然跳过去,但是,接下来的执行不会依据被延迟的时间加上间隔时间,而是根据之前的时间来执行。

举个栗子:定时时间间隔为2秒,t1秒添加成功,那么会在t2、t4、t6、t8、t10秒注册好事件,并在这些时间触发。假设第3秒时,执行了一个超时操作耗费了5.5秒,则触发时间是:t2、t8.5、t10,第4和第6秒就被跳过去了,虽然在t8.5秒触发了一次,但是下一次触发时间是t10,而不是t10.5。

         第二种不准时:不准点

        a RunLoop为了节省资源,并不会在非常准确的时间点触发 

        b 线程有耗时操作,或者其它线程有耗时操作也会影响

iOS7以后,Timer 有个属性叫做Tolerance (时间宽容度,默认是0),标示了当时间点到后,容许有多少最大误差。它只会在准确的触发时间到加上Tolerance时间内触发,而不会提前触发(是不是有点像我们的火车,只会晚点。。。)。另外可重复定时器的触发时间点不受Tolerance影响,即类似上面说的t8.5触发后,下一个点不会是t10.5,而是t10 + Tolerance,不让timer因为Tolerance而产生漂移

           

     3. 必须得把timer添加到runloop中,它才会生效。

     4.要让timer生效,必须保证该线程的runloop已启动,而且其运行的runloopmode也要匹配。

         NSTimer添加到NSRunLoop:

timer必须添加到runloop才有效,很明显要保证两件事情,一是runloop存在(运行),另一个才是添加。确保这两个前提后,还有runloop模式的问题。

一个timer可以被添加到runloop的多个模式,比如在主线程中runloop一般处于NSDefaultRunLoopMode,而当滑动屏幕的时候,比如UIScrollView或者它的子类UITableView、UICollectionView等滑动时runloop处于UITrackingRunLoopMode模式下,因此如果你想让timer在滑动的时候也能够触发,就可以分别添加到这两个模式下。或者直接用NSRunLoopCommonModes一个模式集,包含了上面的两种模式。

但是一个timer只能添加到一个runloop(runloop与线程一一对应关系,也就是说一个timer只能添加到一个线程)。如果你非要添加到多个runloop,则只有一个有效

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值