在iOS开发过程中,定时器通常用在这样的情况下:在某个时间后去执行某个方法,或者是按照一个周期循环执行某个方法。大概呢,有以下三种方案:
NSTimer
CADisplayLink
GCD
关于NSTimer
-
NSTimer的初始化方法:
1 2 3 4 5 6 7
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo; + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo; + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo; + (NSTimer *)scheduledTimerWithTimeInterval:(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 NS_DESIGNATED_INITIALIZER;
-
部分参数说明
repeats: 设置定时器是否循环执行,YES表示循环执行,NO将只执行一次。
NSTimeInterval: 执行之前等待的时间
target: 需要执行方法的对象
selector: 需要执行的方法 -
说明:不是以
scheduledTimerWithTimeInterval:
方式初始化的,需要手动地将timer添加到一个runloop中。而以scheduledTimerWithTimeInterval:
方式初始化,会自动把timer加入MainRunloop的NSDefaultRunLoopMode中。1 2
NSTimer *timer = [NSTimer timerWithTimeInterval:8 target:self selector:@selector(timerAction) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
-
其他方法及属性
1
- (void)fire;
-
该方法用来立即触发该定时器;
官方解释:You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived.
意思是说:在重复执行的定时器中调用此方法后立即触发该定时器,但不会中断其之前的执行计划;在不重复执行的定时器中调用此方法,立即触发后,就会使这个定时器失效。
1
- (void)invalidate;
- 该方法是唯一的,将计时器从runloop中移出的方法。
1 2
timer.fireDate = [NSDate distantPast]; // 开启定时器 timer.fireDate = [NSDate distantFuture]; // 关闭定时器
- 如果希望先停止,然后再某种情况下再次开启运行timer,可以使用属性:
fireDate
。
1 2 3 4 5 6 7 8
// 这是一个只读的属性,用来获取定时器调用的间隔时间。 @property (readonly) NSTimeInterval timeInterval; // 7.0之后新增的一个属性,因为NSTimer并不完全精准,通过这个值设置误差范围。 @property NSTimeInterval tolerance; // 判断定时器是否有效 @property (readonly, getter=isValid) BOOL valid;
-
关于CADisplayLink
-
CADisplayLink是一个能让我们
以和屏幕刷新率同步的频率
将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息,CADisplayLink类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒
。因此CADisplayLink的selector默认调用周期是每秒60次,这个周期可以通过frameInterval属性设置,CADisplayLink的selector每秒调用次数=60/frameInterval。比如当frameInterval设为2,每秒调用就变成30次。 -
CADisplayLink的方法及属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel; - (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode; - (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode; - (void)invalidate; // 只读的CFTimeInterval值,表示屏幕显示的上一帧的时间戳,这个属性通常被target用来计算下一帧中应该显示的内容。 @property(readonly, nonatomic) CFTimeInterval timestamp; // 表示两次屏幕刷新之间的时间间隔,**该属性在target的selector被首次调用以后才会被赋值。selector的调用间隔时间计算方式是:时间=duration×frameInterval**。 @property(readonly, nonatomic) CFTimeInterval duration; @property(getter=isPaused, nonatomic) BOOL paused; // 用来设置间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。 @property(nonatomic) NSInteger frameInterval;
-
示例
1 2 3 4 5 6 7
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)]; self.displayLink.frameInterval = 2; [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; // 关闭定时器 [self.displayLink invalidate]; self.displayLink = nil;
-
CADisplaylink和NSTimer的区别
CADisplayLink: 使用场合相对专一,适合做UI的不停重绘,比如
自定义动画引擎或者视频播放的渲染
。在UI相关的动画或者显示内容使用CADisplayLink显得很方便,因为不需要格外关心屏幕的刷新频率了,它本身就是跟屏幕刷新同步的。
NSTimer: 使用范围要广泛的多,各种需要单次或者循环定时处理的任务
都可以使用。
关于GCD
-
执行一次
1 2 3 4 5
double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ //执行事件 });
-
重复执行
1 2 3 4 5 6 7 8
NSTimeInterval period = 1.0; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); dispatch_source_set_event_handler(_timer, ^{ NSLog(@"GCD continue"); }); dispatch_resume(_timer);