跳到主要内容

卡顿检测与渲染优化

问题

什么是离屏渲染?如何检测和优化卡顿?

答案

掉帧原理

屏幕刷新率 60Hz → 每帧 16.67ms。如果 CPU + GPU 处理超过 16.67ms → 掉帧

离屏渲染

GPU 无法直接渲染到帧缓冲区,需要额外开辟缓冲区 → 上下文切换开销大

触发离屏渲染的操作:

操作是否离屏优化方案
cornerRadius + masksToBounds用贝塞尔路径切圆角
shadow(无 shadowPath)设置 shadowPath
mask避免或用图片替代
group opacity设为 false
shouldRasterize✅(首次)用于静态内容缓存

圆角优化

// ❌ 触发离屏渲染
imageView.layer.cornerRadius = 10
imageView.layer.masksToBounds = true

// ✅ 方案1:用 UIBezierPath 切圆角(CPU 绘制)
let path = UIBezierPath(roundedRect: imageView.bounds, cornerRadius: 10)
let mask = CAShapeLayer()
mask.path = path.cgPath
imageView.layer.mask = mask

// ✅ 方案2:CoreGraphics 异步绘制圆角图片
func roundedImage(_ image: UIImage, radius: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(image.size, false, 0)
let rect = CGRect(origin: .zero, size: image.size)
UIBezierPath(roundedRect: rect, cornerRadius: radius).addClip()
image.draw(in: rect)
let result = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return result
}

卡顿检测

// RunLoop 监控方案
func startMonitor() {
let observer = CFRunLoopObserverCreateWithHandler(
kCFAllocatorDefault,
CFRunLoopActivity.allActivities.rawValue,
true, 0
) { _, activity in
// 记录 beforeWaiting → afterWaiting 的时间差
// 超过阈值(如 50ms)则上报卡顿堆栈
}
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, .commonModes)
}

常见面试问题

Q1: 如何用 Instruments 定位卡顿?

答案:使用 Core Animation 工具,开启 "Color Offscreen-Rendered Yellow"(离屏渲染区域标黄)。使用 Time Profiler 查看主线程耗时函数。帧率低于 60fps 的位置就是卡顿点。

Q2: shouldRasterize 什么时候用?

答案:适合静态内容(如不变的阴影+圆角)。开启后 GPU 首次离屏渲染后缓存为位图,后续帧直接使用缓存。动态变化的内容不要开启(缓存频繁失效反而更慢)。

相关链接