iOS 计时器三种定时器的用法NSTimer、CADisplayLink、GCD
原文:http://www.cocoachina.com/ios/20160919/17595.html
一、三种计时器
二、全局倒计时
一、三种计时器
#import "ViewController.h" @interface ViewController () { CADisplayLink * displaylinked; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //1 timer 会有延迟不够精确 [self tiemred]; //2。CADisplayLink 适合UI刷新动画 [self cadisplayLink]; //3 GCD精确度高 [self GcdTimer]; } -(void)GcdTimer{ //GCD的定时器和NSTimer是不一样的,NSTimer受RunLoop影响,但是GCD的定时器不受影响,因为RunLoop也是基于GCD的 //1 执行1次 double delayInseconds = 2.0;//间隔时间 dispatch_time_t poptimer = dispatch_time(DISPATCH_TIME_NOW, delayInseconds * NSEC_PER_SEC); dispatch_after(poptimer, dispatch_get_main_queue(), ^{ //执行事件 }); //重复执行 NSTimeInterval period = 1.0 ;//时间间隔 dispatch_queue_t queuet = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queuet); dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, period * NSEC_PER_SEC, 0 * NSEC_PER_SEC); dispatch_source_set_event_handler(timer, ^{ //执行事件 }); dispatch_resume(timer); } -(void)cadisplayLink{ //创建 displaylinked = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayAction:)]; //加入RunLoop [displaylinked addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; /* CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。 CADisplayLink以特定模式注册到runloop后, 每当屏幕显示内容刷新结束的时候,runloop就会向 CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。 iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。不需要在格外关心屏幕的刷新频率了,本身就是跟屏幕刷新同步的*/ /*CADisplayLink 与 NSTimer 有什么不同 iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。 NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在阻塞状态,触发时间就会推迟到下一个runloop周期。并且 NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间的延迟范围。 CADisplayLink使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。在UI相关的动画或者显示内容使用 CADisplayLink比起用NSTimer的好处就是我们不需要在格外关心屏幕的刷新频率了,因为它本身就是跟屏幕刷新同步的。 下面结合NSTimer来介绍 CADisplayLink,与NSTimer不同的地方有: 1、原理不同 CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。 CADisplayLink以特定模式注册到runloop后, 每当屏幕显示内容刷新结束的时候,runloop就会向 CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。 NSTimer以指定的模式注册到runloop后,每当设定的周期时间到达后,runloop会向指定的target发送一次指定的selector消息。 2、周期设置方式不同 iOS设备的屏幕刷新频率(FPS)是60Hz,因此CADisplayLink的selector 默认调用周期是每秒60次,这个周期可以通过frameInterval属性设置, CADisplayLink的selector每秒调用次数=60/ frameInterval。比如当 frameInterval设为2,每秒调用就变成30次。因此, CADisplayLink 周期的设置方式略显不便。 NSTimer的selector调用周期可以在初始化时直接设定,相对就灵活的多。 3、精确度不同 iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。 NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在忙于别的调用,触发时间就会推迟到下一个runloop周期。更有甚者,在OS X v10.9以后为了尽量避免在NSTimer触发时间到了而去中断当前处理的任务,NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间范围。 4、使用场合 从原理上不难看出, CADisplayLink 使用场合相对专一, 适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。 NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。 */ } -(void)displayAction:(CADisplayLink*)display{ NSLog(@"没一帧都在刷新"); //display 的取消 display.paused = YES; [display invalidate]; display = nil; } -(void)tiemred{ //timer计时器 //1 不循环 NSTimer * timerNo = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(timerAction:) userInfo:nil repeats:NO]; //循环的计时器 NSTimer * timerYes = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; //加入RunLoop 保证Runloop改变是timer正常工作 [[NSRunLoop currentRunLoop] addTimer:timerYes forMode:NSRunLoopCommonModes]; } -(void)timerAction:(NSTimer*)timer{ NSLog(@"计时器来了"); //计时器销毁 // [timer invalidate]; // timer = nil; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
二、全局倒计时
.h
#import <Foundation/Foundation.h> @interface GlobalTimer : NSObject @property(nonatomic,strong) NSTimer * timer; @property(nonatomic,assign) int countDown;//计时时间 @property(nonatomic,strong) NSDate * beforeDate;//上次进入后台的时间 /** 倒计时 计时器 @param times 剩余时间 */ -(void)timerStarWithTimeInt:(int)times; @end
.m
#import "GlobalTimer.h" #import "AppDelegate.h" @implementation GlobalTimer //倒计时 -(void)timerStarWithTimeInt:(int)times{ _countDown = times; _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop]addTimer:_timer forMode:NSRunLoopCommonModes]; //添加通知。进入后台 前台的 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(BGAction) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(FGAction) name:UIApplicationWillEnterForegroundNotification object:nil]; } //倒计时事件 -(void)timerAction:(NSTimer*)timer{ if (_countDown == 0) { [self timerStop]; }else{ _countDown -=1; } } //进入前台事件 -(void)FGAction{ NSDate * now = [NSDate date]; int interval = (int)ceil([now timeIntervalSinceDate:_beforeDate]);//与上次时间的时间差 int val = _countDown - interval; if (val>1) { _countDown -= interval; }else{ _countDown = 1; } } //进入后台事件 -(void)BGAction{ _beforeDate = [NSDate date];//获取当前时间 } -(void)timerStop{ [_timer invalidate]; _timer = nil; //移除通知 [[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; } //循环次数计时器 -(void)timerStarWithcountEveryTimeInt:(int)count timesOfTotalInt:(int)times{ // _countDown = times; // _timer = [NSTimer scheduledTimerWithTimeInterval:count target:timer selector:<#(nonnull SEL)#> userInfo:<#(nullable id)#> repeats:<#(BOOL)#>] // } @end