定时器是什么?
定时器提供执行延迟动作或周期性动作的方式。 定时器等待直到一定时间间隔过去,然后触发,向指定的对象发送指定的消息。 例如,您可以创建一个计时器,向控制器对象发送消息,告诉它在一定时间间隔后更新特定值。
注意:定时器与NSRunLoop对象协同工作。 因此,他们不提供实时机制 - 他们的准确性有限。
定时器的精度
定时器不是实时机制;它仅在已经添加了定时器的运行循环模式中的一个正在运行时才触发,并且能够检查定时器的触发时间是否已经过去。由于典型的运行循环管理的各种输入源,用于定时器的时间间隔的有效分辨率被限制在大约50-100毫秒。如果定时器的触发时间发生在运行循环处于不监视定时器或长度调出的模式时,则定时器不会触发,直到下一次运行循环检查定时器。因此,定时器可能发生的实际时间可以是预定点火时间之后的显着时间段。
重复定时器基于预定的点火时间而不是实际的点火时间重新调度自身。例如,如果定时器被调度为在特定时间并且在那之后每5秒触发,则即使实际点火时间被延迟,预定点火时间将总是落在原始的5秒时间间隔上。如果点火时间被延迟,使得它通过一个或多个预定点火时间,则定时器在该时间段仅点火一次;则在点火之后对于未来的下一个预定点火时间重新计划定时器。
定时器的替代品
如果您只是想在将来的某个时间发送消息,可以不使用计时器。 您可以使用performSelector:withObject:afterDelay:和相关方法直接在另一个对象上调用方法。 另外有一些变体,如performSelectorOnMainThread:withObject:waitUntilDone :,允许您调用特定线程上的方法。 您还可以使用cancelPreviousPerformRequestsWithTarget:和相关方法取消延迟信息的发送。
创建定时器
创建定时器有三种方法:
1. 使用当前运行循环创建定时器
+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation*)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullableid)userInfo repeats:(BOOL)yesOrNo;这种最常见
iOS10之后又出现了下面这种block的方式:
+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void(^)(NSTimer*timer))block
2. 创建一个定时器,之后主动添加到运行运行中
+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation*)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullableid)userInfo repeats:(BOOL)yesOrNo;
iOS10之后又出现了下面这种block的方式:
+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void(^)(NSTimer*timer))block
3. 使用给定的启动日期初始化定时器
- (instancetype)initWithFireDate:(NSDate*)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullableid)ui repeats:(BOOL)rep
iOS10之后又出现了下面这种block的方式:
- (instancetype)initWithFireDate:(NSDate*)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void(^)(NSTimer*timer))block
引用定时器和对象声明周期
如果用第一种方式创建的非循环定时器,从对象生命周期的角度来看,通常没有必要保留对这种定时器的引用,因为定时器执行完毕之后会自动结束生命周期。 然而,在许多情况下,你也想主动让定时器无效。在这种情况下,您需要保留对定时器的引用,以便您可以在适当的时候停止定时器。如果是用第一种方式创建的是循环定时器,那么需要保留对定时器的引用,以便你随时可以停止定时器并释放器内存。如果用第二种方式创建的定时器,则必须保持对定时器的强引用,以便在使用定时器之前不会释放定时器对象。
特别说明:定时器保持对其目标的强引用。这意味着只要定时器保持有效,其目标将不会被释放。作为推论,这意味着定时器的目标在dealloc方法中尝试让定时器无效是没有意义的 ,只要定时器有效,dealloc方法就不会被调用。
定时器容差
在iOS 7或更高版本,您可以指定计时器的公差(tolerance)。允许系统在定时器触发时的灵活性提高,系统优化以增加功率节省和响应性的能力。定时器可以在其计划的触发日期和计划的触发日期加上公差之间的任何时间触发。定时器在预定的触发日期之前不会触发。对于重复定时器,下一个触发日期是从原始触发日期计算的,而不考虑在个别触发时间之间的容差,以避免漂移。tolerance的默认值为零,这意味着不使用tolerance这个属性。无论tolerance属性的值如何,系统都会保留对特定计时器应用少量容差的权利。
作为定时器的用户,您将最好地了解定时器的适当容限。一般的经验法则是,对于重复定时器,将容差设置为间隔的至少10%。即使少量的容差也会对应用程序的功耗产生显着的积极影响。系统可以设置公差的最大值。