在软件开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法。在这个时候,我们就需要用到定时器。而关于定时器,ios开发我们会用到的也就以下几种。下面介绍以下:
第一种:NSTimer
1、创建方法一
1)、
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action) userInfo:nil repeats:YES];
TimerInterval : 执行之前等待的时间。比如设置成1.0,就代表1秒后执行方法
userInfo:可以写一些标识信息(一般不用)
target : 需要执行方法的对象。
selector : 需要执行的方法
repeats : 是否需要循环
2)、
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
}];
TimerInterval : 执行之前等待的时间。比如设置成1.0,就代表1秒后执行方法
repeats : 是否需要循环
使用block的方法就直接在block里面写延时后要执行的代码就可以了
3)、
SEL action = @selector(action);
NSInvocation* invo = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:action]];
[invo setTarget:self];
[invo setSelector:action];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 invocation:invo repeats:YES];
TimerInterval : 执行之前等待的时间。比如设置成1.0,就代表1秒后执行方法
action : 需要执行的方法
repeats : 是否需要循环
这种三种方式创建的定时器不用加入到RunLoop,是由于系统已经将其加入到了默认的RunLoop中。而且这种定时器创建就会启动。
2、创建方法二
1)、 NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(action) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
TimerInterval : 执行之前等待的时间。比如设置成1.0,就代表1秒后执行方法
userInfo:可以写一些标识信息(一般不用)
target : 需要执行方法的对象。
selector : 需要执行的方法
repeats : 是否需要循环
2)、
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
}];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
TimerInterval : 执行之前等待的时间。比如设置成1.0,就代表1秒后执行方法
repeats : 是否需要循环
使用block的方法就直接在block里面写延时后要执行的代码就可以了
3)、
SEL action = @selector(action);
NSInvocation* invo = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:action]];
[invo setTarget:self];
[invo setSelector:action];
NSTimer* timer = [NSTimer timerWithTimeInterval:1.0 invocation:invo repeats:YES];//加入主循环池中
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
TimerInterval : 执行之前等待的时间。比如设置成1.0,就代表1秒后执行方法
action : 需要执行的方法
repeats : 是否需要循环
这种三种方式创建的定时器都需要我们自己去加入到RunLoop中,不然定时器无法启动。
说明:
释放定时器
[timer invalidate];
timer的一些属性变量:
//这是设置定时器的启动具体时间,常用来管理定时器的启动与停止
@property (copy) NSDate *fireDate;
//启动定时器
// timer.fireDate = [NSDate distantPast];
//停止定时器
// timer.fireDate = [NSDate distantFuture];
这个是一个只读属性,获取定时器调用间隔时间。
@property (readonly) NSTimeInterval timeInterval;
这是7.0之后新增的一个属性,因为NSTimer并不完全精准,通过这个值设置误差范围。
@property NSTimeInterval tolerance ;
获取定时器是否有效
@property (readonly, getter=isValid) BOOL valid;
获取参数信息
@property (nullable, readonly, retain) id userInfo;
第二种:CADisplayLink
创建方法:
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(action)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
target : 需要执行方法的对象。
selector : 需要执行的方法
释放方法
[displayLink invalidate];
displayLink = nil;
释放了displayLink也就意味着停止了定时器,或者使用下面的方法移除定时器
[displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
CADisplayLink的一些属性变量:
每帧之间的时间
@property(readonly, nonatomic) CFTimeInterval duration;
间隔多少帧调用一次selector 方法,默认1
@property(nonatomic) NSInteger frameInterval;
屏幕显示的上一帧的时间戳,只读的CFTimeInterval值。
@property(readonly, nonatomic) CFTimeInterval timestamp;
是否暂停,控制CADisplayLink的运行
@property(getter=isPaused, nonatomic) BOOL paused;
注意:1、通常来讲:iOS设备的刷新频率事60HZ也就是每秒60次。那么每一次刷新的时间就是1/60秒 大概16.7毫秒。当我们的frameInterval 值为1的时候我们需要保证的是 CADisplayLink调用的target的函数计算时间不应该大于 16.7否则就会出现严重的丢帧现象。
第三种:GCD技术
只执行一次
double delayInSeconds = 2.0;
dispatch_time_t poptime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(poptime, dispatch_get_main_queue(), ^{
//执行事件
});
delayInSeconds:执行之前等待的时间。比如设置成2.0,就代表2秒后执行方法
重复执行
/** timer声明成属性存储起来 */
@property (nonatomic , strong)dispatch_source_t timer;
NSTimeInterval period = 1.0;//设置时间间隔
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, period * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^{
NSLog(@"%@" , [NSThread currentThread]);//打印当前线程
dispatch_async(dispatch_get_main_queue(), ^{
//回到主线程做事情
});
});
dispatch_resume(_timer);
注意:要将timer存储起来,不然会就只执行一次
如果不定时器使用完了的话 ,取消定时器:dispatch_source_cancel(_timer);
最后讲一下: NSTimer和CADisplayLink 的区别和联系
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的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。
有人说到,我是使用NSTimer和还是GCD技术呢,关于这个我就不做太详细的介绍了,希望大家看一下这篇文章,这里给我们说了我们如何使用他俩:http://www.jianshu.com/p/0c050af6c5ee
参考了一下这篇文字:http://www.jianshu.com/p/646ebb8b205e
如果还有别的疑问可以发邮件到我的邮箱:namezyqyx@163.com
也欢迎大家提出宝贵意见