iOS 三种常用定时器NSTimer、CADisplayLink、GCD详解

在iOS开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法,这时我们就需要用到定时器,而在iOS中,常用的定时器有以下三种NSTimerCADisplayLinkGCD,下面对这几种方法的创建、执行、销毁及优缺点做个详解:

1. NSTimer

创建方式

//方式一
    self.timer = [NSTimer scheduledTimerWithTimeInterval:2
                                                  target:self
                                                selector:@selector(timerTest)
                                                userInfo:nil
                                                 repeats:YES];
//方式二
    self.timer = [NSTimer timerWithTimeInterval:2
                                         target:self
                                       selector:@selector(timerTest)
                                       userInfo:nil
                                        repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
  • 使用 scheduled 开头创建的定时器,系统会自动的把它加入到 runLoopNSDefaultRunLoopMode 中,缺点是不能更改mode模式
  • 使用 timerWithTimeInterval 开头创建的定时器,需要手动的把他添加到runLoop中,同时设置它的mode运行模式。
定时执行(以下就是我们设置定时器需要做的事件)
- (void)timerTest {
    NSLog(@"==========NSTimer===============");
}

暂停、继续

暂停
self.timer.fireDate = [NSDate distantFuture];
继续
self.timer.fireDate = [NSDate distantPast];
  • 应用场景 比如,在页面消失的时候关闭定时器,然后等页面再次打开的时候,又开启定时器(主要是为了防止它在后台运行,占用CPU)

销毁

  [self.timer invalidate];
  self.timer = nil;

RunLoop 有五种运行模式,其中第1、5种为苹果对外提供的mode

1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
5. kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode
  • 常见问题
    在我们平时开发中可能遇到过,当我们使用NSTimer每一段时间执行一些事情时滑动UIScrollViewNSTimer就会暂停,当我们停止滑动以后,NSTimer又会重新恢复。那是因为Mode的切换造成的,当我们在主线程使用定时器,此时RunLoopModekCFRunLoopDefaultMode,即定时器属于kCFRunLoopDefaultMode,那么此时我们滑动ScrollView时,RunLoopMode会切换到UITrackingRunLoopMode,因此在主线程的定时器就不在管用了,调用的方法也就不再执行了,当我们停止滑动时,RunLoopMode切换回kCFRunLoopDefaultMode,所有NSTimer就又管用了。

  • 解决方案

  1. 将NSTimer添加到两种模式下
  2. 使用占位的运行模式 NSRunLoopCommonModes标记,凡是被打上NSRunLoopCommonModes标记的都可以运行,而kCFRunLoopDefaultModeUITrackingRunLoopMode就被打上commonModel标记。

NSTimer 缺点

计时不精确:不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoopRunLoop Mode有关,如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会在延时结束后立刻执行,并按照之前指定的周期继续执行。

2.CADisplayLink

  • 他是苹果专门提供的一个类,主要的优势在于他的执行频率是根据设备屏幕的刷新频率来计算的,也即是时间间隔最准确的定时器。

创建方式

 self.displayLink = [CADisplayLink displayLinkWithTarget:self
                                                   selector:@selector(timerTest)];
 [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

定时执行

- (void)timerTest {
    NSLog(@"==========CADisplayLink===============");
}

暂停、继续

添加一个按钮用于控制暂停继续(或其他点击事件)
pasued属性是控制计时器暂停与恢复,设置为YES的时候会暂停事件的触发。

- (void) displayLinkStart {
    self.displayLink.paused = !self.displayLink.paused;
}

销毁

- (void)stopDisplayLink {
    [self.displayLink invalidate];
    self.displayLink = nil;
}

优缺点

  • 优点: 依托于设备屏幕刷新频率触发事件,所以其触发时间上是最准确的。也是最适合做UI不断刷新的事件,过渡相对流畅,无卡顿感。

  • 缺点:

  1. 由于依托于屏幕刷新频率,若果CPU不堪重负而影响了屏幕刷新,那么我们的触发事件也会受到相应影响。
  2. selector触发的时间间隔只能是duration的整倍数
  3. selector事件如果大于其触发间隔就会造成掉帧现象。

相关属性

 @property(readonly, nonatomic) CFTimeInterval timestamp;    //获取上一次selector被执行的时间戳
 @property(readonly, nonatomic) CFTimeInterval duration; //获取当前设备的屏幕刷新时间间隔
 @property(getter=isPaused, nonatomic) BOOL paused;  //控制计时器暂停与恢复的属性
    
    //事件触发间隔。是指两次selector触发之间间隔几次屏幕刷新,默认值为1,也就是说屏幕每刷新一次,执行一次selector,这个也可以间接用来控制动画速度
 @property(nonatomic) NSInteger frameInterval
    CA_AVAILABLE_BUT_DEPRECATED_IOS (3.1, 10.0, 9.0, 10.0, 2.0, 3.0, "use preferredFramesPerSecond");
 @property(nonatomic) NSInteger preferredFramesPerSecond;

3. GCD-dispatch_source_t

创建 添加 执行方法

- (void)addDisPatch_source {
    //1.创建
    self.disTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    //2.设置时间
    dispatch_source_set_timer(self.disTimer, dispatch_walltime(NULL,0 * NSEC_PER_SEC), 1 * NSEC_PER_SEC, 0);
    //3.执行
    dispatch_source_set_event_handler(self.disTimer, ^{
        NSLog(@"=========dispatch_source_t=========");
    });
}

参数:

dispatch_source_create()相关参数

参数 意义
type dispatch源可处理的事件
handle 可以理解为句柄、索引或id,假如要监听进程,需要传入进程的ID
mask 可以理解为描述,提供更详细的描述,让它知道具体要监听什么
queue 自定义源需要的一个队列,用来处理所有的响应句柄(block)

dispatch_source_set_timer()相关参数

参数 意义
source dispatch_source_t
start 事件首次触发的延迟时间
interval 时间间隔
leeway 误差范围

开始 暂停

//开始
dispatch_resume(self.disTimer);
//暂停
dispatch_suspend(self.disTimer);

销毁

dispatch_source_cancel(self.disTimer);

注意:dispatch_source_t 一定要被设置为成员变量,否则将会立即被释放。

优缺点

  • 优点:不受当前runloopMode的影响。
  • 缺点:虽然不受runloopMode的影响,但是其计时效应仍不是百分之百准确的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容