iOS中最全的各种定时器使用教程

前言

相信一说到定时器, 我们使用最多的就是NSTimer 和 GCD 了, 还有另外一个高级的定时器 CADisplayLink;,下面将给大家详细介绍关于iOS定时器使用的相关内容,话不多说了,来一起看看详细的介绍吧。

一. NSTimer

NSTimer的初始化方法有以下几种:

会自动启动, 并加入 MainRunloop 的 NSDefaultRunLoopMode 中,

注意: 这里的自动启动, 并不是马上就会启动, 而是会延迟大概一个interval的时间:

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

参数:

  • internal : 时间间隔, 多久调用一次
  • repeats: 是否重复调用
  • block: 需要重复做的事情

使用:

 [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {

 static NSInteger num = 0;

 NSLog(@"%ld", (long)num);
 num++;

 if (num > 4) {

 [timer invalidate];

 NSLog(@"end");
 }
 }];

 NSLog(@"start");

这时, 控制台的输出:

2016-12-29 16:29:53.901 定时器[11673:278678] start
2016-12-29 16:29:54.919 定时器[11673:278678] 0
2016-12-29 16:29:55.965 定时器[11673:278678] 1
2016-12-29 16:29:56.901 定时器[11673:278678] 2
2016-12-29 16:29:57.974 定时器[11673:278678] 3
2016-12-29 16:29:58.958 定时器[11673:278678] 4
2016-12-29 16:29:58.959 定时器[11673:278678] end

可以看出, 这里的internal设置为1s, 大概延迟了1s才开始执行block里的内容;

这里的停止定时器, 我直接在block里进行的, 如果使用一个全局变量来再其他地方手动停止定时器,需要这样进行:

[self.timer invalidate];
self.timer = nil;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo

参数:

  • ti: 重复执行时间间隔
  • invocation: NSInvocation实例, 其用法见NSInvocation的基本用法
  • yesOrNo: 是否重复执行

示例:

// NSInvocation形式
- (void)timer2 {

 NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:)];

 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method];
 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 invocation:invocation repeats:YES];

 // 设置方法调用者
 invocation.target = self;

 // 这里的SEL需要和NSMethodSignature中的一致
 invocation.selector = @selector(invocationTimeRun:);

 // 设置参数
 // //这里的Index要从2开始,以为0跟1已经被占据了,分别是self(target),selector(_cmd)
 // 如果有多个参数, 可依次设置3 4 5 ...
 [invocation setArgument:&timer atIndex:2];

 [invocation invoke];

 NSLog(@"start");
}
- (void)invocationTimeRun:(NSTimer *)timer {

 static NSInteger num = 0;
 NSLog(@"%ld---%@", (long)num, timer);

 num++;

 if (num > 4) {
 [timer invalidate];
 }
}

输出:

2016-12-29 16:52:54.029 定时器[12089:289673] 0---<__NSCFTimer: 0x60000017d940>
2016-12-29 16:52:54.029 定时器[12089:289673] start
2016-12-29 16:52:55.104 定时器[12089:289673] 1---<__NSCFTimer: 0x60000017d940>
2016-12-29 16:52:56.095 定时器[12089:289673] 2---<__NSCFTimer: 0x60000017d940>
2016-12-29 16:52:57.098 定时器[12089:289673] 3---<__NSCFTimer: 0x60000017d940>
2016-12-29 16:52:58.094 定时器[12089:289673] 4---<__NSCFTimer: 0x60000017d940>

可以看出, 这里定时器是立马就执行了, 没有延迟;

此方法可以传递多个参数, 下面是传递两个参数的示例:

// NSInvocation形式
- (void)timer2 {

 NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:des:)];

 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method];
 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 invocation:invocation repeats:YES];

 // 设置方法调用者
 invocation.target = self;

 // 这里的SEL需要和NSMethodSignature中的一致
 invocation.selector = @selector(invocationTimeRun:des:);

 // 设置参数
 // //这里的Index要从2开始,以为0跟1已经被占据了,分别是self(target),selector(_cmd)
 // 如果有多个参数, 可依次设置3 4 5 ...
 [invocation setArgument:&timer atIndex:2];
 // 设置第二个参数
 NSString *dsc = @"第二个参数是字符串";
 [invocation setArgument:&dsc atIndex:3];

 [invocation invoke];

 NSLog(@"start");
}
- (void)invocationTimeRun:(NSTimer *)timer des:(NSString *)dsc {

 static NSInteger num = 0;
 NSLog(@"%ld---%@--%@", (long)num, timer, dsc);

 num++;

 if (num > 4) {
 [timer invalidate];
 }
}

输出:

2016-12-29 16:57:45.087 定时器[12183:292324] 0---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串
2016-12-29 16:57:45.088 定时器[12183:292324] start
2016-12-29 16:57:46.161 定时器[12183:292324] 1---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串
2016-12-29 16:57:47.161 定时器[12183:292324] 2---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串
2016-12-29 16:57:48.150 定时器[12183:292324] 3---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串
2016-12-29 16:57:49.159 定时器[12183:292324] 4---<__NSCFTimer: 0x60000016dbc0>--第二个参数是字符串
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo

参数:

  • ti: 时间间隔
  • aTarget: 调用者
  • aSelector: 执行的方法
  • userInfo: 参数
  • yesOrNo: 是否重复执行

示例:

- (void)timer3 {

 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(targetRun:) userInfo:@"这是携带的参数" repeats:YES];

 NSLog(@"start");
}
- (void)targetRun:(NSTimer *)timer {

 static NSInteger num = 0;

 NSLog(@"%ld---%@--%@", (long)num, timer, timer.userInfo);

 num++;

 if (num > 4) {
 [timer invalidate];
 }
}

输出:

2016-12-29 17:05:11.590 定时器[12328:296879] start
2016-12-29 17:05:12.655 定时器[12328:296879] 0---<__NSCFTimer: 0x608000162700>--这是携带的参数
2016-12-29 17:05:13.661 定时器[12328:296879] 1---<__NSCFTimer: 0x608000162700>--这是携带的参数
2016-12-29 17:05:14.664 定时器[12328:296879] 2---<__NSCFTimer: 0x608000162700>--这是携带的参数
2016-12-29 17:05:15.651 定时器[12328:296879] 3---<__NSCFTimer: 0x608000162700>--这是携带的参数
2016-12-29 17:05:16.650 定时器[12328:296879] 4---<__NSCFTimer: 0x608000162700>--这是携带的参数

下面这三种方式创建定时器的用法, 和上面相应的方法类似, 需要注意的是, 这样创建的定时器, 并不会执行, 需要我们手动来开启定时器;

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo

开启的方式是, 将当前定时器添加到RunLoop中:

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

下面给出一个示例:

- (void)timer4 {

 NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {

 static NSInteger num = 0;

 NSLog(@"%ld", (long)num);
 num++;

 if (num > 4) {

 [timer invalidate];
 timer = nil;

 NSLog(@"end");
 }
 }];

 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

 NSLog(@"start");
}

输出:

2016-12-29 17:12:13.955 定时器[12498:301751] start
2016-12-29 17:12:15.013 定时器[12498:301751] 0
2016-12-29 17:12:16.018 定时器[12498:301751] 1
2016-12-29 17:12:17.011 定时器[12498:301751] 2
2016-12-29 17:12:18.024 定时器[12498:301751] 3
2016-12-29 17:12:19.023 定时器[12498:301751] 4
2016-12-29 17:12:19.023 定时器[12498:301751] end

定时器基本的创建方式就这些了, 还可以设置其他的属性, 例如开启时间, 这些直接参考其API 进行设置即可;

注意: 以上实例中, 我没有使用全局的NSTimer 对象, 如果设置全局变量, 或者设置为属性, 在停止定时器的时候要手动置为nil, 即:

[timer invalidate];
 timer = nil;

二. GCD

dispatch_after : 延迟执行一次

dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block)

示例:

- (void)gcdTimer {

 // 延迟2s
 dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);

 dispatch_after(delayTime, dispatch_get_main_queue(), ^(void){

 NSLog(@"延迟2s后执行");
 });

 NSLog(@"start");
}

重复执行的定时器

void
dispatch_source_set_timer(dispatch_source_t source,
 dispatch_time_t start,
 uint64_t interval,
 uint64_t leeway)

参数:

  • source: 定时器
  • start: 开始时间, 当我们使用 dispatch_time 或者 DISPATCH_TIME_NOW 时,系统会使用默认时钟来进行计时。然而当系统休眠的时候,默认时钟是不走的,也就会导致计时器停止。使用 dispatch_walltime 可以让计时器按照真实时间间隔进行计时;
  • interval: 间隔(如果设置为 DISPATCH_TIME_FOREVER 则只执行一次)
  • leeway: 允许的误差范围; 计时不可能是百分百精确的, 即使设置为0, 也不是百分百精确的, 所以可以设置合理的允许误差, 单位: 纳秒(NSEC_PER_SEC)

相关内容, 可参考文章: Dispatch Source Timer 的使用以及注意事项

// 重复执行的定时器
- (void)gcdTimer1 {

 // 获取全局队列
 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_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));

// dispatch_time_t start = dispatch_walltime(NULL, 0);

 // 重复间隔
 uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);

 // 设置定时器
 dispatch_source_set_timer(_timer, start, interval, 0);

 // 设置需要执行的事件
 dispatch_source_set_event_handler(_timer, ^{

 //在这里执行事件
 static NSInteger num = 0;

 NSLog(@"%ld", (long)num);
 num++;

 if (num > 4) {

 NSLog(@"end");

 // 关闭定时器
 dispatch_source_cancel(_timer);
 }
 });
 // 开启定时器
 dispatch_resume(_timer);

 NSLog(@"start");
}

输出:

2016-12-30 10:15:01.114 定时器[3393:99474] start
2016-12-30 10:15:02.187 定时器[3393:99796] 0
2016-12-30 10:15:03.114 定时器[3393:99796] 1
2016-12-30 10:15:04.186 定时器[3393:99796] 2
2016-12-30 10:15:05.188 定时器[3393:99796] 3
2016-12-30 10:15:06.188 定时器[3393:99796] 4
2016-12-30 10:15:06.188 定时器[3393:99796] end

这里的开始时间设置了1s的间隔, 所以1s之后才开始执行,可以设置使用DISPATCH_TIME_NOW来立马执行;

注意:

这里的开始时间(start)可以使用下面的方式的来设置:

dispatch_time_t start = dispatch_walltime(NULL, 0);

或者直接设置为: DISPATCH_TIME_NOW

关于 dispatch_walltime 和 dispatch_time 的区别, 上面也有提及,也可参考stackOverflow上的这个回答; 主要区别就是前者在系统休眠时还会继续计时, 而后者在系统休眠时就停止计时, 待系统重新激活时, 接着继续计时;

停止计时器:

停止GCD定时器的方式, Dispatch Source Timer 的使用以及注意事项中有提及, 主要有以下两种:

// 关闭定时器
// 完全销毁定时器, 重新开启的话需要重新创建
// 全局变量, 关闭后需要置为nil
dispatch_source_cancel(_timer);

// 暂停定时器
// 可使用dispatch_resume(_timer)再次开启
// 全局变量, 暂停后不能置为nil, 否则不能重新开启
dispatch_suspend(_timer);

三. CADisplayLink

CADisplayLink默认每秒运行60次,通过它的 frameInterval 属性改变每秒运行帧数,如设置为2,意味CADisplayLink每隔一帧运行一次,有效的逻辑每秒运行30次

屏幕刷新时调用:CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒

延迟:iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。

如果CPU过于繁忙,无法保证屏幕60次/秒的刷新率,就会导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。

使用场景:从原理上可以看出,CADisplayLink适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。

+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel

参数:

  • target: 调用者
  • sel: 执行的方法

示例:

- (void) displayLink {

 CADisplayLink *display = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayRun:)];

 // 大概1s执行一次
// 取值范围 1--100, 值越大, 频率越高
 display.preferredFramesPerSecond = 2;

 [display addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)displayRun:(CADisplayLink *)link {

 static NSInteger num = 0;

 NSLog(@"%ld", (long)num);
 num++;

 if (num > 4) {

 [link invalidate];

 NSLog(@"end");
 }
}

这里的示例不太恰当, 不应该在这种场合使用,

另外, 我们可以使用他的 paused 属性, 来使其暂停, 或继续:

// 暂停
 display.paused = YES;
// 继续
 display.paused = NO;

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • iOS中的NSTimer定时器的初步使用解析

    创建一个定时器(NSTimer) - (void)viewDidLoad { [super viewDidLoad]; [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(actionTimer:) userInfo:nil repeats:YES]; } - (void)actionTimer:(NSTimer *)timer { } NSTimer默认运行在default mode下,default

  • iOS仿小红书呼吸灯动画(核心动画和定时器)两种方式实现

    最近公司需求做个类似小红书的标签呼吸灯动画,经过一段时间研究使用两种方式实现了该效果... 第一种方式使用定时器加 UIView动画,核心方法如下 -(void)begigFlashAnimation { // 缩放 + 透明度动画 self.flashView.transform = CGAffineTransformMakeScale(0.1, 0.1); [UIView animateWithDuration:3 animations:^{ self.flashView.transform

  • iOS开发定时器的三种方法分享

    前言 在开发中,很多时候我们需要用到定时器实时刷新某个数值.这个时候我们就需要用到定时器,这里,我为大家推荐三种方法,分别是:NSTimer.CADisplayLink.GCD.接下来我就一一介绍它们的用法.希望能帮到大家. 一.NSTimer(一般用于定时的更新一些非界面上的数据) 1. 创建方法 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(action:

  • iOS中最全的各种定时器使用教程

    前言 相信一说到定时器, 我们使用最多的就是NSTimer 和 GCD 了, 还有另外一个高级的定时器 CADisplayLink;,下面将给大家详细介绍关于iOS定时器使用的相关内容,话不多说了,来一起看看详细的介绍吧. 一. NSTimer NSTimer的初始化方法有以下几种: 会自动启动, 并加入 MainRunloop 的 NSDefaultRunLoopMode 中, 注意: 这里的自动启动, 并不是马上就会启动, 而是会延迟大概一个interval的时间: + (NSTimer *

  • iOS中UIAlertView警告框组件的使用教程

    1. 最简单的用法 初始化方法: 复制代码 代码如下: - (instancetype)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id /*<UIAlertViewDelegate>*/)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles,

  • iOS中UIAlertView3秒后消失的两种实现方法

    一,效果图. 二,代码. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIAlertView* alert = [[UIAlertView alloc]initWithTitle:nil message:@"此信息3秒后消失" delegate:nil cancelButtonTitle:nil ot

  • IOS 中NSTimer定时器的使用

    IOS 中NSTimer定时器的使用 NSTimery 定时器,主要用于进行定时执行指定方法,常用场景如:获取验证码的按钮倒计时:图片轮播定时. 1 使用注意事项: 1.1 倒计时时间间隔(时间单位是秒) 1.2 指定的执行方法 1.3 实现指定执行方法的对象 1.4 是否重复执行 2 对象的内存管理及销毁 2.1 使用方法" invalidate "进行停止 2.2 将对象设置为" nil " 2.3 特别是在返回到其他视图控制器的时候,要在方法" -

  • iOS中几种定时器的实现小结

    在软件开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法.在这个时候,我们就需要用到定时器. 然而,在iOS中有很多方法完成以上的任务,到底有多少种方法呢?经过查阅资料,大概有三种方法:NSTimer.CADisplayLink.GCD.接下来我就一一介绍它们的用法. 一.NSTimer 1. 创建方法 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selecto

  • iOS中视频播放的基本方法总结

    前言 本文总结了iOS中最常见的视频播放方法,不同的方法都各具特点,我希望能够总结它们的不同,方便在开发中选择合适的技术方案. Apple为我们提供了多种方法来实现视频播放,包括MPMoviePlayerController,MPMoviePlayerViewController,AVPlayer,AVPlayerViewController等.而值得注意的是,上述的MPMoviePlayerController与MPMoviePlayerViewController在iOS9.0之后被弃用.虽

  • iOS中使用schema协议调用APP和使用iframe打开APP的例子

    在iOS中,需要调起一个app可以使用schema协议,这是iOS原生支持的,并且因为iOS系统中都不能使用自己的浏览器内核,所以所有的浏览器都支持,这跟android生态不一样,android是可以自己搞内核的,但是iOS不行. 在iOS中提供了两种在浏览器中打开APP的方法:Smart App Banner和schema协议. Smart App Banner 即通过一个meta 标签,在标签上带上app的信息,和打开后的行为,例如:app-id之类的,代码形如: 复制代码 代码如下: <m

  • 正则表达式在IOS中的应用及IOS中三种正则表达式的使用与比较

    正则表达式在ios中应用 一.什么是正则表达式 正则表达式,又称正规表示法,是对字符串操作的一种逻辑公式.正则表达式可以检测给定的字符串是否符合我们定义的逻辑,也可以从字符串中获取我们想要的特定部分.它可以迅速地用极简单的方式达到字符串的复杂控制. 二.正则表达式的语法 看一个过滤纯数字的例子 - (BOOL)validateNumber:(NSString *) textString { NSString* number=@"^[0-9]+$"; NSPredicate *numbe

  • 详解ios中的SQL数据库文件加密 (使用sqlcipher)

    今天本想写一片 GAE+goAgent+SwitchySharp 的指南的!但是突然翻出了前段时间写的关于iOS中的SQL数据库文件加密的代码,于是乎决定今天就先讲讲这个!- 那么goAgent将放在周末,后续的文章中除了文件加密,还有传输数据加密,感兴趣的童鞋 敬请留意. 言归正传,sql的文件加密,我们首先要用到一个库,它就是大名鼎鼎的Sqlcipher,  奉上连接:http://sqlcipher.NET,在ios里 我们需要看的文档是这一篇http://sqlcipher.Net/io

  • IOS中的webView加载HTML

    在日常开发中,我们为了效率会用到很多很多的WebView,比如在做某个明细页面的时候我们返回给你的可能是一个html字符串,我们就需要将当前字符串展示到webView上面,所以我们对HTML标签需要有一定的认识,下面我们来一起用html标签和JS写一个打地鼠游戏,这里我们主要讲解HTML标签的书写,只要如何和webView适配涉及到响应式布局我们下次讲解: 1.首先我们先新建一个html文件 2 完整html标签并且设置编码格式为UTF-8 3 在body里面增加十只老鼠图片,并且增加点击事件,

随机推荐