作者:Mitchell
一、CADisplayLink简介
- CADisplayLink 是一个定时器对象可以让你的应用以与显示器的刷新界面相同的频率进行绘图。
- 应用如果想要创建 display link ,需要提供一个目标对象和一个当屏幕刷新时被调用的选择器方法。之后再将 display link 添加到主线程中。
- 一旦display link与主线程相关联,当屏幕内容需要被刷新的时候目标对象上的选择器方法就会被调用。目标对象可以读取 display link 的时间戳属性去检索下一帧被显示的画面。举个例子,一个执行它自己动画的应用会使用时间戳来确定在哪或者如何去在即将显示的画面中去展示它的对象。
duration
属性提供了一个在两个画面之间的间隔时间。你可以在你的应用中使用这个值来估算显示的帧速率,近似下一帧被显示的时间,和调整行为,以便下一帧准备时间显示。 - 你的应用可以设置 paused 属性为YES来停止通知。同样的,如果你的应用不想用框架所提供的时间,你可以自己选择更慢的帧速率。已提供更慢但是一致的帧速率会比跳帧更加流畅,你可以通过改变
frameInterval
属性来改变改变画面的帧间隔(减少帧速率)。(每几帧调用一次,默认是1,如果是2,对于iOS设备来说那刷新频率就是60HZ也就是每秒60次,如果将 frameInterval 设为2 那么就会两帧调用一次,也就是变成了每秒刷新30次。) - 当你的应用想停止 display link ,它应该调用
invalidate
方法去从主线程中移除它并且消除与目标之间的关联。 -
CADisplayLink 不能被继承
。
二、NSTimer简介
使用 NSTimer 类创建定时器对象或者仅仅是计时器。一个计时器每隔一定的时间间隔运行,然后触发,发送给目标对象一个特殊的消息。举例来说,你能够创建一个 NSTimer 对象发送给窗口一个消息,告诉它在一定时间间隔之后更新它自己。
定时器的运行需要结合着 run Loop。为了有效的使用一个定时器,你应该注意如何操作 run loops。请搜索在
Xcode Document
中 NSRunLoop 和 Threading Programming Guide 文章。特别注意,run loops 对它们的定时器保持着强引用,所以你不必去对加到 run loop 中的定时器保持强引用。计时器并不是一个真正的时间机制;它只有在被添加到循环运行模式中的一种的时候才能触发,此时计时器的触发时间一旦过去就能检查出。由于对各种输入源的一个典型run loop 管理,对于一个定时器的时间间隔的有效解决方案被限制在50-100毫秒,如果一个定时器的触发经历了很长的一段时间或者在一个 run loop 的模式下没能监控到计时器,则这个计时器不会触发直到下一次 run loop 再次检测出这个计时器。因此,真正的计时器触发时间可以在预定触发时间之后的很长一段时间之后。可以看看这篇文章 Timer Tolerance。
-
重复计时器与不可重复计时器
- 你可以在创建计时器的时候指定它是可重复的还是不可重复的。一个不可重复的计时器只触发一次然后自动销毁,从而防止计时器再次触发。与之相反,一个可重复的计时器会重复在相同的 run loop 中触发。
- 一个可重复的计时器总是按照预先设计的触发时间执行它自己,而不是实际的出发时间。举个例子,如果一个计时器将在特定的时间每5秒钟之后触发,预定触发时间将总会落后于开始的5秒时间间隔,即使实际的触发时间被推迟。如果到目前为止触发时间被推迟了很久已经错过了一个或者几个触发的时间点,计时器会在下一个触发周期上只触发一次;触发后计时器重新安排,为下一次触发做准备。
-
在 Run Loops 中预设定时器
- 一个定时器对象只能同事被注册在一个 run loop 中,虽然它能被添加到这个 run loop 中的多个 run loop 模式中去。有三种方式创建:
-
scheduledTimerWithTimeInterval:invocation:repeats:
orscheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
类方法创建计时器,将它以默认的模式预设在当前的 run loop 中。 -
timerWithTimeInterval:invocation:repeats:
ortimerWithTimeInterval:target:selector:userInfo:repeats:
类方法创建而没在 run loop 中预设它(创建之后,你必须将它添加到 run loop 中去,使用相应的 NSRunLoopaddTimer :forMode
方法) - 初始化计时器方法
initWithFireDate:interval:target:selector:userInfo:repeats:
-
- 一旦在 run loop 中预设了定时器,定时器就会在制定的时间间隔触发直到它被销毁。一个不重复的计时器会在触发之后自动被销毁。然而,对于一个重复的计时器,你必须调用它的 invaludate 方法来销毁它。调用这个方法会请求在 当前的 run loop 中将计时器移除;所以,你应该总在创建计时器的线程中来调用 invalidate 方法来销毁它。销毁计时器之后会立即让它无效所以不会再影响 run loop。然后在移除方法之前或者晚一点点 run loop 会将计时器移除(与它的强引用一起移除),一旦被移除,计时器对象就不能再使用。
- 一个定时器对象只能同事被注册在一个 run loop 中,虽然它能被添加到这个 run loop 中的多个 run loop 模式中去。有三种方式创建:
-
子类注意
-
不能够继承 NSTimer
。
-
-
Timer Tolerance:
- 在 iOS 7和 OSX v10.9 之后,你能够为定时器指定一个公差。让系统在计时器触发的时候更加灵活的提升优化系统并增加它的存储和响应的能力。计时器会在预定的时间和预定时间加上公差时间之内触发计时器。计时器不会在预定时间之前触发。对于重复计时器为了避免漂移,下次的触发时间是根据原始的触发时间所估算出来而公差只应用于单次的触发时间。默认值是0,意味着没有更多的公差。系统有权利对于一些计算器使用少量的公差而不管公差的属性值是多少。
- 作为计时器的使用者,也许你有一个对于计时器最合适的公差。一般的经验是对于可重复的计时器设置公差至少有10%的时间间隔。即使少量的公差也将对应用程序产生重大的积极影响。系统可能为公差设置一个最大值。
-
Toll-Free Bridging:
- 在Core Foundation框架和 Foundation 框架中有许多种数据类型可以被互换着使用。这种能力,被称为: Toll-Free Bridging,意味着你能够使用相同的数据类型作为 Core Foundation 方法调用的参数也可以作为一个OC 方法传递的接受者。举个例子:NSLocale 相对 Core Foundation 是的互换的类型是 CFLocale。因此,如果一个方法中的参数是 NSLocale* 类型的时候,你可以传递过去一个 CFLocaleRef,并且如果方法中的参数是 CGLocaleRef 参数,你可以传递过去一个 NSLocale 实例。
-
NSRunLoop:
- NSRunLoop 类的声明是对于输入源的面向对象的编程思想。一个NSRunloop 对象处理着来自窗口系统、NSPort 对象和 NSConnection 对象的像鼠标或键盘事件的输入源。一个 NSRunloop 对象也能够处理 NSTimer 事件。
- 你的应用不能够创建或者显示的管理 NSRunLoop 对象。每个 NSThread 对象,包括应用的主线程,都有一个 NSRunLoop 对象由于需求被自动的创建。如果你需要访问当前线程的 run loop,你可以对 currentRunLoop 类做操作。
- 注意从 NSRunLoop的角度来看, NSTimer 对象并不是“输入源” - 他们是一种特殊类型,这意味着它们不会在触发的时候引起 run loop 返回。
注意: NSRunLoop 类一般不认为是线程安全的,其方法应该只在其当前的线程上下文中被调用。你不能尝试在别的线程中去调用 NSRunLoop 对象的方法,如果这样做将导致无法预料的结果。
小结
- 在动画中最好用CADisplayLink,因为由于每秒的刷新频率较高,所以用它所生成的动画会显得非常流畅。