OC中的三种定时器:CADisplayLink、NSTimer、GCD

我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下

@interface CADisplayLink : NSObject
{
@private
void *_impl;  //指针
} + (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
//唯一一个初始化方法 - (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode;
//将创建好点实例添加到RunLoop中 - (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode;
//从RunLoop中移除
- (void)invalidate;
//销毁实例 @property(readonly, nonatomic) CFTimeInterval timestamp;   //上一次Selector被调用到时间, 只读
@property(readonly, nonatomic) CFTimeInterval duration;   //屏幕刷新时间间隔, 目前iOS刷新频率是60HZ, 所以刷新时间间隔是16.7ms @property(readonly, nonatomic) CFTimeInterval targetTimestamp CA_AVAILABLE_IOS_STARTING(10.0, 10.0, 3.0);
//下一次被调用到时间
@property(getter=isPaused, nonatomic) BOOL paused;  //设置为YES的时候会暂停事件的触发 @property(nonatomic) NSInteger frameInterval      
CA_AVAILABLE_BUT_DEPRECATED_IOS (3.1, 10.0, 9.0, 10.0, 2.0, 3.0, "use preferredFramesPerSecond"); //事件触发间隔。是指两次selector触发之间间隔几次屏幕刷新,默认值为1,也就是说屏幕每刷新一次,执行一次selector,这个也可以间接用来控制动画速度
@property(nonatomic) NSInteger preferredFramesPerSecond CA_AVAILABLE_IOS_STARTING(10.0, 10.0, 3.0); 
//每秒现实多少帧 @end

从头文件来看CADisplayLink的使用还是挺简单的, 下面上代码:

- (void)viewDidLoad {

    [super viewDidLoad];

    self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(logCount)]; self.displayLink.frameInterval = ; //屏幕刷新2次调用一次Selector [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; } - (void)logCount { self.count ++;
NSLog(@"Count = %ld", self.count); if (self.count > ) { self.count = ;
[self.displayLink invalidate]; //直接销毁
}
}

代码很简单就不做说明了

需要注意的是CADisplayLink必须要添加到可以执行的RunLoop中才会执行, 当添加到某一个RunLoop后如果该RunLoop暂停或者该RunLoop的Model改变了, 计时器也会暂停

比如我们给TableView添加计时器到当前RunLoop的NSDefaultRunLoopMode model中, 当屏幕一半显示时计时器可以正常调用, 但当我们用手滑动TableView时, 计时器就会暂停。

因为当滑动时, RunLoop会进入到UITrackingRunLoopMode

所以当我们发现计时器没有运行时, 可以检查下是否有加入到正确的mode中

那我们来说一下runloop的几种mode:

  • Default模式

定义:NSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode (Core Foundation)

描述:默认模式中几乎包含了所有输入源(NSConnection除外),一般情况下应使用此模式。

  • Connection模式

定义:NSConnectionReplyMode(Cocoa)

描述:处理NSConnection对象相关事件,系统内部使用,用户基本不会使用。

  • Modal模式

定义:NSModalPanelRunLoopMode(Cocoa)

描述:处理modal panels事件。

  • Event tracking模式

定义:UITrackingRunLoopMode(iOS)
NSEventTrackingRunLoopMode(cocoa)

描述:在拖动loop或其他user interface tracking loops时处于此种模式下,在此模式下会限制输入事件的处理。例如,当手指按住UITableView拖动时就会处于此模式。

  • Common模式

定义:NSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes (Core Foundation)

描述:这是一个伪模式,其为一组run loop mode的集合,将输入源加入此模式意味着在Common Modes中包含的所有模式下都可以处理。在Cocoa应用程序中,默认情况下Common Modes包含default modes,modal modes,event Tracking modes.可使用CFRunLoopAddCommonMode方法向Common Modes中添加自定义modes。

注:iOS中仅NSDefaultRunLoopMode,UITrackingRunLoopMode,NSRunLoopCommonModes三种可用mode。

CADisplayLink

基本用法刚刚介绍过。

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

缺点:

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

-------------------我是分割线---------------------

下面说说NSTImer, 一样我们直接看头文件并用注释说明

@interface NSTimer : NSObject

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
//实例化方法, 响应事件用的NSInvocation, 需要手动添加到RunLoop中才会生效
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
//实例化方法, 响应事件用的NSIvocation, 系统为自动帮你将timer添加到currentRunLoop中,defaultMode
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
//实例化方法, 响应事件用的PerformanceSelector, userInfo中可以用来传参数,需要手动添加到RunLoop中
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
//实例化方法,响应事件用的PerformanceSelector, userInfo可以用来传递参数, 系统会自动帮你将timer添加到currentRunLoop中, defaultMode + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//实例化方法, 以block的方式传入要执行的内容, 需要手动添加到RunLoop中 + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//实例化方法, 以block的方式传入要执行的内容,系统会自动帮你将timer添加到currentRunLoop中,defaultMode - (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//跟上面类似, 只是多指定了一个开始时间
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep NS_DESIGNATED_INITIALIZER;
//跟上面类似, 只是多指定了一个开始时间
- (void)fire;  //立即执行一次定时器方法, 注意不是立即开启定时器 @property (copy) NSDate *fireDate;  //当前事件的触发事件, 一般用来做暂停和恢复
@property (readonly) NSTimeInterval timeInterval;  //只读属性, 获取当前timer的触发间隔 @property NSTimeInterval tolerance NS_AVAILABLE(10_9, 7_0); //允许的误差值 - (void)invalidate; //立即销毁timer
@property (readonly, getter=isValid) BOOL valid; //只读属性, 获取当前timer是否有效 @property (nullable, readonly, retain) id userInfo; //只读属性, 初始化时传入的用户参数 @end

NSTimer的内容相对多一些但也更加灵活, 有一个地方需要注意的是timer开头的实例化方法需要手动添加到RunLoop, Schedule开头的会由系统帮你添加到RunLoop

  • fireDate,设置当前timer的事件的触发时间。通常我们使用这个属性来做计时器的暂停与恢复
///暂停计时器
self.timer.fireDate = [NSDate distantFuture];
///恢复计时器
self.timer.fireDate = [NSDate distantPast];
  • tolerance,允许误差时间。我们知道NSTimer事件的触发事件是不准确的,完全取决于当前runloop处理的时间。如果当前runloop在处理复杂运算,则timer执行时间将会被推迟,直到复杂运算结束后立即执行触发事件,之后再按照初始设置的节奏去执行。当设置tolerance之后在允许范围内的延迟可以触发事件,超过的则不触发。默认是时间间隔的1/10

网上很多人对fire方法的解释其实并不正确。fire并不是立即激活定时器,而是立即执行一次定时器方法

当加入到runloop中timer不需要激活即可按照设定的时间触发事件。fire只是相当于手动让timer触发一次事件

如果timer设置的repeat为NO,则fire之后timer立即销毁

如果timer的repeat为YES,则到了之前设置的时间他依旧会按部就班的触发事件

fire只是单独触发了一次事件,并不影响原timer的节奏

  • 关于invalid方法

我们知道NSTimer使用的时候如果不注意的话,是会造成内存泄漏的。原因是我们生成实例的时候,会对控制器retain一下。如果不对其进行管理则VC的永远不会引用计数为零,进而造成内存泄漏。

所以,当我们不需要的timer的时候,请如下操作:

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

这样Timer会对VC进行一次release。所以一定不要忘记调用invalid方法

顺便提一句,如果生成timer实例的时候repeat为NO,那当触发事件结束后,系统也会自动调用invalid一次

NSTimer的优势:使用相对灵活,应用广泛

劣势:受runloop影响严重,同时易造成内存泄漏(调用invalid方法解决)

-------------------我是分割线---------------------

下面说说GCD计时器:dispatch_source_t

其实dispatch_source_t说为计时器不完全正确, 它实际上是GCD给我们用的一个源对象

还是先直接上代码:

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, assign)   NSInteger count;
@property (nonatomic, strong) dispatch_source_t tTimer; //GCD计时器一定要设置为成员变量, 否则会立即释放 @end @implementation ViewController @synthesize tTimer; - (void)viewDidLoad { [super viewDidLoad]; //创建GCD timer资源, 第一个参数为源类型, 第二个参数是资源要加入的队列
self.tTimer = \
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , , dispatch_get_main_queue()); //设置timer信息, 第一个参数是我们的timer对象, 第二个是timer首次触发延迟时间, 第三个参数是触发时间间隔, 最后一个是是timer触发允许的延迟值, 建议值是十分之一
dispatch_source_set_timer(self.tTimer,
dispatch_walltime(NULL, * NSEC_PER_SEC),
0.32 * NSEC_PER_SEC,
); //设置timer的触发事件
dispatch_source_set_event_handler(self.tTimer, ^{ [self logCount];
}); //激活timer对象
dispatch_resume(self.tTimer);
} - (void)logCount { self.count ++;
NSLog(@"Count = %ld", self.count); if (self.count > ) { self.count = ;
//暂停timer对象
dispatch_suspend(self.tTimer); //销毁timer, 注意暂停的timer资源不能直接销毁, 需要先resume再cancel, 否则会造成内存泄漏
//dispatch_source_cancel(self.tTimer);
}
}

注释已经很清楚了, 就不再逐条解释(上面代码会呦循环引用的问题, 大家自己改下)

需要注意的是, GCD timer资源必须设定为成员变量, 否则会在创建完毕后立即释放

suspend挂起或暂停后的timer要先resume才能cancel, 挂起的timer直接cancel会造成内存泄漏

GCDTimer的优势:不受当前runloopMode的影响。
劣势:虽然说不受runloopMode的影响,但是其计时效应仍不是百分之百准确的。

另外,他的触发事件也有可能被阻塞,当GCD内部管理的所有线程都被占用时,其触发事件将被延迟

好吧GCD我也没用玩转, 只说这些。 后面会找时间专门研究下

Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用的更多相关文章

  1. cocos2dx三种定时器使用

         cocos2dx三种定时器的使用以及停止schedule.scheduleUpdate.scheduleOnce 今天白白跟大家分享一下cocos2dx中定时器的用法. 首先,什么是定时 ...

  2. C#中三种定时器对象的比较 【转】

    https://www.cnblogs.com/zxtceq/p/5667281.html C#中三种定时器对象的比较 ·关于C#中timer类 在C#里关于定时器类就有3个1.定义在System.W ...

  3. iOS三种定时器的用法NSTimer、CADisplayLink、GCD

    一,NSTimer //创建方式1 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector: ...

  4. NSTimer、CADisplayLink、GCD 三种定时器的用法 —— 昉

    在软件开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法.在这个时候,我们就需要用到定时器. 在iOS中有很多方法完成定时器的任务,例如 NSTimer.CADisp ...

  5. C#中三种定时器对象的比较

    ·关于C#中timer类 在C#里关于定时器类就有3个1.定义在System.Windows.Forms里2.定义在System.Threading.Timer类里3.定义在System.Timers ...

  6. C#三种定时器

    三个定时器分别是 实现按用户定义的时间间隔引发事件的计时器.此计时器最宜用于 Windows 窗体应用程序中,并且必须在窗口中使用. System.Windows.Forms.Timer 提供以指定的 ...

  7. C#三种定时器的实现

    http://www.coridc.com/archives/2253.html c#中提供了三种类型的计时器: 1.基于 Windows 的标准计时器(System.Windows.Forms.Ti ...

  8. iOS 计时器三种定时器的用法NSTimer、CADisplayLink、GCD

    原文:http://www.cocoachina.com/ios/20160919/17595.html DEMO链接

  9. android 三种定时器的写法

    //两秒后执行new Handler().postDelayed(new Runnable() { @Override public void run() { --todo }}, 2000); -- ...

随机推荐

  1. ABP入门系列(2)——通过模板创建MAP版本项目

    一.从官网创建模板项目 进入官网下载模板项目 依次按下图选择: 输入验证码开始下载 下载提示: 二.启动项目 使用VS2015打开项目,还原Nuget包: 设置以Web结尾的项目,设置为启动项目: 打 ...

  2. Syscan360会议胸牌破解揭秘

    Syscan360会议胸牌破解揭秘 背景 有幸参加今年11月份的上海Syscan360安全会议,会议期间有一个亮点就是360的独角兽团队设计了一款电子badge(胸牌)供参加人员进行破解尝试,类似于美 ...

  3. python自动化测试(2)-自动化基本技术原理

    python自动化测试(2) 自动化基本技术原理 1   概述 在之前的文章里面提到过:做自动化的首要本领就是要会 透过现象看本质 ,落实到实际的IT工作中就是 透过界面看数据. 掌握上面的这样的本领 ...

  4. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  5. npm 使用小结

    本文内容基于 npm 4.0.5 概述 npm (node package manager),即 node 包管理器.这里的 node 包就是指各种 javascript 库. npm 是随同 Nod ...

  6. .NET中AOP方便之神SheepAspect

    SheepAspect 简介以及代码示列: SheepAspect是一个AOP框架为.NET平台,深受AspectJ.它静织目标组件作为一个编译后的任务(编译时把AOP代码植入). 多有特性时,可根据 ...

  7. Lind.DDD.LindAspects方法拦截的介绍

    回到目录 什么是LindAspects 之前写了关于Aspects的文章<Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP>,今天主要在设计思想上进 ...

  8. Android Studio 编译单个module

    前期自己要把gradle环境变量配置好 在Terminal中gradle命令行编译apk 输入gradle assembleRelease 会编译全部module编译单个modulecd ./xiru ...

  9. Android之使用文件进行IPC

    一.文件进行IPC介绍 共享文件也是一种不错的进程间通信方式,两个进程通过读/写同一个文件来交换数据.在Windows上,一个文件如果被加了排斥锁将会导致其他线程无法对其进行访问,包括读写,而由于An ...

  10. SEED实验系列文章目录

    美国雪城大学SEEDLabs实验列表 SEEDLabs是一套完整的信息安全实验,涵盖本科信息安全教学中的大部分基本原理.项目组2002年由杜文亮教授创建,目前开发了30个实验,几百所大学已采用.实验楼 ...