在iOS中NSTimer的优缺点有哪些
iOS中NSTimer的优缺点如下:
优点:
使用标准的target-action模式,简单好用。
缺点:
1. 在失效前,会强引用target,不注意的话可能会引发问题。
2. 默认情况下,加入runloop时使用的是def***ltRunLoopMode,在某些情况下可能不会被及时触发。
3. 不支持block(可以自己封装)。
iOS三种定时器的用法NSTimer、CADispl***link、GCD
一、NSTimer
1.创建方法
TimerInterval : 执行之前等待的时间。比如设置成1.0,就代表1秒后执行方法
target : 需要执行方法的对象。
selector : 需要执行的方法
repeats : 是否需要循环
2.释放方法
3.特点
存在延迟:不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoop和RunLoop Mode有关,如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。重复性的timer遇到这种情况,如果延迟超过了一个周期,则会在延时结束后立刻执行,并按照之前指定的周期继续执行。
必须加入Runloop:使用上面的创建方式,会自动把timer加入MainRunloop的NSDef***ltRunLoopMode中。如果使用以下方式创建定时器,就必须手动加入Runloop:
二、CADispl***link
什么是CADispl***link
CADispl***link是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。我们在应用中创建一个新的 CADispl***link 对象,把它添加到一个runloop中,并给它提供一个 target 和selector 在屏幕刷新的时候调用。
一但 CADispl***link 以特定的模式注册到runloop之后,每当屏幕需要刷新的时候,runloop就会调用CADispl***link绑定的target上的selector,这时target可以读到 CADispl***link 的每次调用的时间戳,用来准备下一帧显示需要的数据。例如一个视频应用使用时间戳来计算下一帧要显示的视频数据。在UI做动画的过程中,需要通过时间戳来计算UI对象在动画的下一帧要更新的大小等等。在添加进runloop的时候我们应该选用高一些的优先级,来保证动画的平滑。可以设想一下,我们在动画的过程中,runloop被添加进来了一个高优先级的任务,那么,下一次的调用就会被暂停转而先去执行高优先级的任务,然后在接着执行CADispl***link的调用,从而造成动画过程的卡顿,使动画不流畅。
duration属性提供了每帧之间的时间,也就是屏幕每次刷新之间的的时间。我们可以使用这个时间来计算出下一帧要显示的UI的数值。但是 duration只是个大概的时间,如果CPU忙于其它计算,就没法保证以相同的频率执行屏幕的绘制操作,这样会跳过几次调用回调方法的机会。frameInterval属性是可读可写的NSInteger型值,标识间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。如果每帧都调用一次的话,对于iOS设备来说那刷新频率就是60HZ也就是每秒60次,如果将 frameInterval 设为2 那么就会两帧调用一次,也就是变成了每秒刷新30次。我们通过p***se属性开控制CADispl***link的运行。当我们想结束一个CADispl***link的时候,应该调用-(void)invalidate从runloop中删除并删除之前绑定的 targetselector
另外CADispl***link 不能被继承。
1.创建方法
2.停止方法
当把CADispl***link对象add到runloop中后,selector就能被周期性调用,类似于重复的NSTimer被启动了;执行invalidate操作时,CADispl***link对象就会从runloop中移除,selector调用也随即停止,类似于NSTimer的invalidate方法。
3.特点:
屏幕刷新时调用:CADispl***link是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。CADispl***link以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADispl***link指定的target发送一次指定的selector消息, CADispl***link类对应的selector就会被调用一次。所以通常情况下,按照iOS设备屏幕的刷新率60次/秒 延迟:iOS设备的屏幕刷新频率是固定的,CADispl***link在正常情况下会在每次刷新结束都被调用,精确度相当高。但如果调用的方法比较耗时,超过了屏幕刷新周期,就会导致跳过若干次回调调用机会。 如果CPU过于繁忙,无法保证屏幕60次/秒的刷新率,就会导致跳过若干次调用回调方法的机会,跳过次数取决CPU的忙碌程度。 使用场景:从原理上可以看出,CADispl***link适合做界面的不停重绘,比如视频播放的时候需要不停地获取下一帧用于界面渲染。4. 重要属性 frameInterval NSInteger类型的值,用来设置间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。 duration readOnly的CFTimeInterval值,表示两次屏幕刷新之间的时间间隔。需要注意的是,该属性在target的selector被首次调用以后才会被赋值。selector的调用间隔时间计算方式是:调用间隔时间 = duration × frameInterval。
三、GCD
1.执行一次
swift版本:
DispatchSourceTimer
间隔定时器, 相当于repeats设置为true的Timer.
初始化
设置timer参数
示例: 获取验证码60s倒计时
注意事项: 下面两种操作会造成程序崩溃, 原因是: gcdTimer执行了suspend()操作后, 是不可以被直接释放的, 如果想关闭一个执行了suspend()操作的计时器, 需要先执行resume(), 再执行cancel(), 最后置nil.
CADispl***link 与 NSTimer 有什么不同?
iOS设备的屏幕刷新频率是固定的,CADispl***link在正常情况下会在每次刷新结束都被调用,精确度相当高。
NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在阻塞状态,触发时间就会推迟到下一个runloop周期。并且 NSTimer新增了tolerance属性,让用户可以设置可以容忍的触发的时间的延迟范围。
CADispl***link使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。在UI相关的动画或者显示内容使用 CADispl***link比起用NSTimer的好处就是我们不需要在格外关心屏幕的刷新频率了,因为它本身就是跟屏幕刷新同步的。给非UI对象添加动画效果我们知道动画效果就是一个属性的线性变化,比如UIView 动画的 EasyInEasyOut 。通过数值按照不同速率的变化我们能生成更接近真实世界的动画效果。我们也可以利用这个特性来使一些其他属性按照我们期望的曲线变化。比如当播放视频时关掉视频的声音我可以通过CADispl***link来实现一个 EasyOut的渐出效果:先快速的降低音量,在慢慢的渐变到静音。
注意
通常来讲:iOS设备的刷新频率事60HZ也就是每秒60次。那么每一次刷新的时间就是1/60秒 大概16.7毫秒。当我们的frameInterval值为1
的时候我们需要保证的是 CADispl***link调用的 target 的函数计算时间不应该大于 16.7否则就会出现严重的丢帧现象。在mac应用中我们使用的不是CADispl***link而是 CVDispl***link它是基于C接口的用起来配置有些麻烦但是用起来还是很简单的。
IOS中关于NSTimer使用知多少
一、初始化方法:有五种初始化方法,分别是
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
- (void)viewDidLoad {
[super viewDidLoad];
//初始化一个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:NSDef***ltRunLoopMode];
//开始循环
[timer fire];
}
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 invocation:invo repeats:YES];
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
NSTimer * timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(myLog) userInfo:nil repeats:NO]
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(myLog:) userInfo:@"123" repeats:YES]
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep
NSTimer * timer = [[NSTimer alloc]initWithFireDate:[NSDate distantPast] interval:1 target:self selector:@selector(myLog:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDef***ltRunLoopMode];
注意:这五种初始化方法的异同:
1、参数repeats是指定是否循环执行,YES将循环,NO将只执行一次。
2、timerWithTimeInterval这两个类方法创建出来的对象如果不用 addTimer: forMode方法手动加入主循环池中,将不会循环执行。并且如果不手动调用fair,则定时器不会启动。
3、scheduledTimerWithTimeInterval这两个方法不需要手动调用fair,会自动执行,并且自动加入主循环池。
4、init方法需要手动加入循环池,它会在设定的启动时间启动。
ios子线程怎样能开启NSTimer
我们知道在主线程中,是默认开启runloop的(以def***lt模式),而如果单独开辟一个子线程添加一个nstimer时,我们就要手动开启runloop,这里还要注意一下(子线程中的nstimer关闭时一定要在子线程中关闭)。
nstimer的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于nstimer runloop、nstimer的信息别忘了在本站进行查找喔。