编写App工程中,会遇到各种定时器的问题:比如某个页面上的数据定时刷新,需要在定时器中反复向服务器发送请求;首页广告位(Banner)的图片不断滚动,需要在定时器中设定变换的间隔时间;收到用户手势交互时,出现响应动画,可以通过定时器设置不同动画连续执行等等。iOS中的Timer可以通过三种方式来实现:NSTimer,dispatch,CADisplayLink,其执行的精确度依次提高。下面介绍一下各自的使用方式。
NSTimer
NSTimer是OC以面向对象方式封装的Timer对象,从其类文档中可以看到它的两种创建方式:timer和scheduledTimer。
self.timer = [NSTimer timerWithTimeInterval:2 repeats:true block:^(NSTimer * _Nonnull timer)
NSLog(@"timer");
}];
self.timer = [NSTimer timerWithTimeInterval:2 repeats:true block:^(NSTimer * _Nonnull timer)
NSLog(@"timer");
}];
[self.timer fire];
[self.timer invalidate];
timerWithTimeInterval创建的timer, 在fire后唤醒;
scheduledTimerWithTimeInterval创建的timer, 创建后立即唤醒;
timer在invalidate后停止。
dispatch
利用多线程GCD创建的Timer,精确度更高,也可以通过参数设置Timer首次执行时间。
__block int count = 3;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//通过start参数控制第一次执行的时间,DISPATCH_TIME_NOW表示立即执行
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"dispatch_source_set_timer start");
NSLog(@"%zd", count);
if (count == 0) {
dispatch_source_cancel(timer);
}
count--;
});
NSLog(@"main queue");
dispatch_resume(timer);
CADisplayLink
CADisplayLink是和iOS界面刷新效率同步执行,可以在1s内执行60次,执行效率最高。如果屏幕滑动时卡顿,可以用它来检测屏幕屏幕刷新频率。当然,不能在其执行方法中加载大量任务,否则手机内存会急剧增高。
@property (nonatomic, strong) CADisplayLink *link;
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, assign) NSTimeInterval lastTime;
@property (nonatomic, assign) NSInteger count;
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- (void)tick: (CADisplayLink *)link {
if (self.lastTime == 0) {
self.lastTime = link.timestamp;
return;
}
self.count++;
NSLog(@"%f", link.timestamp);
NSTimeInterval delta = link.timestamp - self.lastTime;
if (delta < 1) return;
self.lastTime = link.timestamp;
float fps = _count / delta;
self.label.text = [NSString stringWithFormat:@"%0f", fps];
self.count = 0;
}
Tips:NSRunLoop
iOS应用执行的方式,采用了RunLoop的消息循环,简要来说启动后整个应用就进入一个死循环,应用中的所有事件(各种手势,交互功能(Target-Event)等)在死循环中被检测后加入到消息队列,然后不断执行。
每一线程中都有一个RunLoop,只不过子线程中的RunLoop默认关闭。通过[[NSRunLoop currentRunLoop] run]的方法可以唤醒子线程的RunLoop。
RunLoop中的模式有两种: kCFRunLoopDefaultMode和UITrackingRunLoopMode。kCFRunLoopDefaultMode也即是iOS开发文档中的NSDefaultRunLoopMode,NSRunLoopCommonModes是上述两种mode的组合。主线程的RunLoop在UIScrollView滑动时会切换成UITrackingRunLoopMode,如果将Timer添加到NSDefaultRunLoopMode,就会在滑动手势下停止执行。
关注和喜欢都是对我的鼓励和支持~