一,关于NSTimer
@interface NSTimer : NSObject
/**
* 便利构造器方法
*/
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullableid)userInfo repeats:(BOOL)yesOrNo;
注意:1、timerWithTimeInterval这两个类方法创建出来的对象如果不用 addTimer: forMode方法手动加入主循环池中,将不会循环执行。并且如果不手动调用fair,则定时器不会启动。
2、timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo方法使用
//初始化一个Invocation对象
NSInvocation * invo = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(init)]];
[invo setTarget:self];
[invo setSelector:@selector(myLog)];
NSTimer * timer = [NSTimer timerWithTimeInterval:1 invocation:invo repeats:YES];
//加入主循环池中
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
//开始循环
[timer fire];
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullableid)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
注意:1、scheduledTimerWithTimeInterval这两个方法不需要手动调用fair,会自动执行,并且自动加入主循环池。
/**
* 初始化方法
*/
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullableid)ui repeats:(BOOL)repNS_DESIGNATED_INITIALIZER;
注意:
1、参数repeats是指定是否循环执行,YES将循环,NO将只执行一次。
2、init方法需要手动加入循环池,它会在设定的启动时间启动。
NSTimer * timer = [[NSTimer alloc]initWithFireDate:[NSDate distantPast] interval:1
target:self selector:@selector(myLog:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
- (void)invalidate; //停止并删除
- (void)fire; //立即触发定时器
注意:可以使用-(void)fire;方法来立即触发该定时器
1、在重复执行的定时器中调用此方法后立即触发该定时器,但不会中断其之前的执行计划;
2、在不重复执行的定时器中调用此方法,立即触发后,就会使这个定时器失效。
/**
* 属性
*/
@property (copy)NSDate *fireDate;//这是设置定时器的启动时间,常用来管理定时器的启动与停止
注意:
1、启动定时器
timer.fireDate = [NSDate distantPast];
2、暂停定时器
timer.fireDate = [NSDate distantFuture];
3、继续。
[timer setFireDate:[NSDate date]];
@property (readonly)NSTimeInterval timeInterval;//这个是一个只读属性,获取定时器调用间隔时间。
@property NSTimeInterval toleranceNS_AVAILABLE(10_9,7_0);//这是7.0之后新增的一个属性,因为NSTimer并不完全精准,通过这个值设置误差范围。
@property (readonly,getter=isValid)BOOL valid; //获取定时器是否有效
@property (nullable,readonly,retain)id userInfo; //获取参数信息
@end
说明:
1.不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoop和RunLoop Mode有关,如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会在延时结束后立刻执行,并按照之前指定的周期继续执行。
2.NSTimer 属于Foundation框架
3.NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。
二,关于CADisplayLink
@interface CADisplayLink : NSObject
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)see;//创建CADisplayLink对象
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode; //假如循环队列
- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode; //移除循环队列
- (void)invalidate; //停止
@property(readonly, nonatomic) CFTimeInterval timestamp;
//注释: 可读可写的NSInteger型值,标识间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。官方文档中强调,当该值被设定小于1时,结果是不可预知的。
@property(readonly, nonatomic) CFTimeInterval duration;
//注释: 只读的CFTimeInterval值,表示两次屏幕刷新之间的时间间隔。需要注意的是,该属性在target的selector被首次调用以后才会被赋值。selector的调用间隔时间计算方式是:时间=duration×frameInterval。
现存的iOS设备屏幕的FPS都是60Hz,这一点可以从CADisplayLink的duration属性看出来。duration的值都是0.166666…,即1/60。尽管如此,我们并没法确定苹果不会改变 FPS ,如果以后某一天将 FPS 提升到了120Hz了怎么办呢?这时,你设置了frameInterval属性值为2期望每秒刷新30次,却发现每秒刷新了60次,结果可想而知,出于安全考虑,还是先根据duration判断屏幕的 FPS再去使用CADisplayLink 。
@property(getter=isPaused, nonatomic) BOOL paused;
@property(nonatomic) NSInteger frameInterval;
@end
说明:
1.CADisplayLink所属框架QuartzCore.framework
2.CADisplayLink最主要的特征是能提供一个周期性的调用我们赋给它的selector的机制,与定时器NSTimer很像。
3.当把CADisplayLink对象add到runloop中后,selector就能被周期性调用,类似于NSTimer被启动了;执行invalidate操作时, CADisplayLink对象 就会从runloop中移除,selector 调用也随即停止,类似于NSTimer的invalidate方法。
4.通过pause属性开控制CADisplayLink的运行,用-(void)invalidate 结束一个CADisplayLink的方法
5.CADisplayLink 不能被继承
6.CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕 显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调 用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒
7.iOS并不能保证能以每秒60次的频率调用回调方法,这取决于:
1、CPU的空闲程度
如果CPU忙于其它计算,就没法保证以60HZ执行屏幕的绘制动作,导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。
2、执行回调方法所用的时间
如果执行回调时间大于重绘每帧的间隔时间,就会导致跳过若干次回调调用机会,这取决于执行时间长短。
8.CADisplayLink 使用场合相对专一, 适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。
三,关于GCD
1. 执行一次
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//执行事件
});
2. 重复执行
NSTimeInterval period = 1.0; //设置时间间隔
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_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
//在这里执行事件
});
dispatch_resume(_timer);