设计性能监控 SDK
问题
如何设计 iOS 端的性能监控 SDK?
答案
监控维度
卡顿检测(主线程监控)
class JankMonitor {
private let semaphore = DispatchSemaphore(value: 0)
private var isRunning = false
private let threshold: TimeInterval = 0.05 // 50ms 判定卡顿
func start() {
isRunning = true
DispatchQueue.global().async { [weak self] in
while self?.isRunning == true {
self?.semaphore.signal() // 重置信号
var responded = false
DispatchQueue.main.async {
responded = true
self?.semaphore.signal()
}
// 等待主线程响应
let result = self?.semaphore.wait(timeout: .now() + (self?.threshold ?? 0.05))
if result == .timedOut && !responded {
// 主线程卡顿!采集调用栈
let backtrace = Thread.callStackSymbols
ReportManager.report(type: .jank, data: backtrace)
}
Thread.sleep(forTimeInterval: 1)
}
}
}
}
启动耗时采集
class StartupMonitor {
static let processStartTime: TimeInterval = {
var info = mach_timebase_info_data_t()
mach_timebase_info(&info)
let now = mach_absolute_time()
return Double(now) * Double(info.numer) / Double(info.denom) / 1_000_000_000
}()
static func recordFirstFrame() {
let launchTime = CFAbsoluteTimeGetCurrent() - processStartTime
Analytics.track("app_launch_time", params: ["duration": launchTime])
}
}
关键指标
| 指标 | 目标 | 采集方式 |
|---|---|---|
| 冷启动 | < 1s | mach_absolute_time → 首帧 |
| FPS | >= 55 | CADisplayLink 帧回调间隔 |
| 卡顿率 | < 1% | 主线程信号量超时 |
| 内存峰值 | < 200MB | task_info API |
常见面试问题
Q1: CADisplayLink 检测 FPS 的原理?
答案:CADisplayLink 每次屏幕刷新都回调。记录回调次数和时间差,FPS = count / (currentTime - lastTime)。但它只能在主线程不阻塞时工作,所以严重卡顿时 FPS 会直接降为 0。需要配合子线程信号量检测方案互补。