iOS 性能监控3 FPS与卡顿

FPS

FPS(Frames Per Second)是指画面每秒传输的帧数。每秒帧数越多,所显示的动画就越流畅,一般只要保持 FPS 在 50-60,App 就会有流畅的体验,反之会感觉到卡顿。

相关系统原理

CADisplayLink 是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。

一旦 CADisplayLink 以特定的模式注册到 runloop 之后,每当屏幕需要刷新时,runloop 就会调用 CADisplayLink 绑定的 target 上的 selector,此时 target 可以读取到 CADisplayLink 的每次调用的时间戳,用来准备下一帧显示需要的数据。如:一个视频应用使用时间戳来计算下一帧要显示的视频数据。

代码实现

现阶段,常用的 FPS 监控几乎都是基于 CADisplayLink 实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// swift
final class FPSMonitor: NSObject {
private var timer: Timer?
private var link: CADisplayLink?
private var count: UInt = 0
private var lastTime: TimeInterval = 0

func enableMonitor() {
if link == nil {
link = CADisplayLink(target: self, selector: #selector(fpsInfoCalculate(_:)))
link?.add(to: RunLoop.main, forMode: .common)
} else {
link?.isPaused = false
}
}

func disableMonitor() {
if let link = link {
link.isPaused = true
link.invalidate()
self.link = nil
lastTime = 0
count = 0
}
}

@objc
func fpsInfoCalculate(_ link: CADisplayLink) {
if lastTime == 0 {
lastTime = link.timestamp
return
}
count += 1
let delta = link.timestamp - lastTime
if delta >= 1 {
// 间隔超过 1 秒
lastTime = link.timestamp
let fps = Double(count) / delta
count = 0

let intFps = Int(fps + 0.5)
print("帧率:\(intFps)")
}
}
}

CADisplayLink 实现的 FPS 在生产场景中只有指导意义,不能代表真实的 FPS。因为基于 CADisplayLink 实现的 FPS 无法完全检测出当前 Core Animation 的性能情况,只能检测出当前 RunLoop 的帧率。