7-11.【OC】【RunLoop】CADisplayLink 的使用场景和与 NSTimer 的区别是什么?

3 阅读2分钟

CADisplayLink 是一个特殊的定时器,它与屏幕的刷新频率(Vsync 信号)完全同步。在 iOS 开发中,它是处理高性能动画和游戏循环的首选工具。


1. 核心区别:同步机制与精度

虽然两者都能做定时任务,但它们的底层驱动力完全不同:

特性NSTimerCADisplayLink
驱动源基于系统时钟的 RunLoop 触发基于屏幕刷新率 (Vsync)
精度较低。受 RunLoop 任务繁忙程度影响,容易积压延迟。极高。在每一帧开始刷新前触发,确保渲染连续性。
触发频率自定义(如 0.1s, 2s)固定为屏幕刷新率(60Hz/120Hz)的子集。
使用场景倒计时、延时任务、心跳包UI 动画、视频播放进度、游戏引擎循环

为什么 CADisplayLink 不掉帧?

NSTimer 的触发点是随机的。如果它在屏幕刷新周期的中段触发并执行了 UI 修改,系统必须等到下一次刷新才能把改动显示出来,这会导致视觉上的跳跃感。

CADisplayLink 严格在每一帧开始前触发,确保你的代码修改正好能赶上这一帧的“渲染列车”。


2. 典型使用场景

A. 自定义复杂的 UI 动画

UIView 动画(如 animateWithDuration)无法满足需求时,例如需要根据数学公式实时计算每一帧的位移或形变。

B. 视频/音频同步

在视频播放器中,利用 CADisplayLinktimestamp 属性来同步视频帧的渲染与音频进度。

C. 游戏开发

作为游戏的主循环(Game Loop),负责每一帧的物理计算、碰撞检测和屏幕重绘。


3. 使用中的关键特性

帧率控制 (PreferredFramesPerSecond)

你可以设置 preferredFramesPerSecond。如果屏幕是 60Hz,你设置 30,它就会每隔一帧触发一次。

在 iOS 15+ 的 ProMotion 屏幕上,它甚至支持动态帧率调优。

获取精准时间戳

CADisplayLink 提供了两个极其重要的属性:

  • timestamp:上一帧显示的时间。

  • targetTimestamp:下一帧即将显示的时间。

    利用这两个时间戳,你可以实现非常平滑的补帧动画。


4. 内存管理与注意事项

CADisplayLinkNSTimer 有一个共同的“陷阱”:它也会强引用 Target。

正确的销毁姿势:

由于它也依附于 RunLoop,你必须手动调用 invalidate

Objective-C

// 1. 创建并添加到 RunLoop
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

// 2. 销毁
[self.displayLink invalidate];
self.displayLink = nil;

⚠️ 特别注意:Mode 的影响

NSTimer 一样,默认情况下 CADisplayLink 只在 DefaultMode 下工作。如果你在滑动列表时动画卡住,请务必将其加入到 NSRunLoopCommonModes