两个方案,一个是监听runloop,一个是监听FPS
dart
import Foundation
import os
/*
*/
class MainRunLoopObserver{
private var observer:CFRunLoopObserver?
private var dispatchSourceTimer:DispatchSourceTimer?
//写入日志分析系统
private let log = OSLog(subsystem: "com.yourapp.performance", category: "RunLoopMonitor")
func startMonitoring(){
#if DEBUG //防止测试代码进入release
guard observer == nil else{return}
var activity:CFRunLoopActivity = .entry
switch activity{
case .entry:
print("进入runloop")
case .beforeTimers:
print("处理Timer之前")
case .beforeSources:
print("处理Source之前")
case .beforeWaiting:
print("即将休眠")
case .afterWaiting:
print("从休眠唤醒")
case .exit:
print("退出runloop")
default:
print("default")
}
let observer = CFRunLoopObserverCreateWithHandler(
kCFAllocatorDefault,
CFRunLoopActivity.allActivities.rawValue,
true,
0
) { obs, act in
activity = act
//写入
os_signpost(.begin, log: self.log, name: "RunLoop Activity", "%{public}s", "\(activity)")
}
self.observer = observer
//给主线程Runloop添加观察
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, CFRunLoopMode.commonModes)
// 创建一个定时器,检测 RunLoop 是否被卡住
dispatchSourceTimer = DispatchSource.makeTimerSource(queue: DispatchQueue.global(qos: .background))
// 100ms 轮询
dispatchSourceTimer?.schedule(deadline: .now(),repeating: 0.1)
dispatchSourceTimer?.setEventHandler(handler: {[weak self]in
guard let self = self else{return}
let startTime = CFAbsoluteTimeGetCurrent()
DispatchQueue.main.async {
//计算延迟
let elapsed = CFAbsoluteTimeGetCurrent() - startTime
if elapsed > 0.05{ //超过500ms认为就是卡顿
print("线程卡顿")
os_signpost(.event, log: self.log, name: "UI 卡顿", "RunLoop 卡顿 %d ms", Int(elapsed))
}
os_signpost(.end, log: self.log, name: "RunLoop Activity")
/*
如何在 Instruments 里查看 os_signpost 日志
1. 打开 Instruments
• 在 Xcode 中,点击 Product -> Profile (⌘ + I) 进入 Instruments。
2. 选择 "Points of Interest"
• 选择 Points of Interest 模板,可以看到 os_signpost 记录的 FPS 下降点。
3. 运行应用
• 运行 App,触发 FPS 下降(比如滚动复杂 UI),查看 Instruments 的数据。
*/
}
})
dispatchSourceTimer?.resume()
#endif
}
func stopMonitoring(){
if let observer = observer{
CFRunLoopRemoveObserver(CFRunLoopGetMain(), observer, CFRunLoopMode.commonModes)
self.observer = nil
}
dispatchSourceTimer?.cancel()
dispatchSourceTimer = nil
}
}
import QuartzCore
/*
CADisplayLink 是一个定时器,每次屏幕刷新时调用其回调方法。默认情况下,iOS 设备的屏幕刷新率是 60Hz(每秒 60 帧,iphone16 pro max 会有高刷有120Hz版本),因此如果 CADisplayLink 在 1 秒内执行的次数小于 60 次,就意味着帧率下降,可能出现了卡顿。
*/
class FPSMonitor{
private var displayLink:CADisplayLink?
private var lastTimeStamp:TimeInterval = 0
private var frameCount:Int = 0
//写入到日志系统
private let log = OSLog(subsystem: "com.yourapp.performance", category: "FPSMonitor")
func startMonitor(){
#if DEBUG
guard displayLink == nil else{return}
// 每次屏幕刷新时调用 updateFPS
displayLink = CADisplayLink(target: self, selector: #selector(updateFps))
displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
#endif
}
//停止监听
func stopMonitor(){
#if DEBUG
displayLink?.invalidate()
displayLink = nil
#endif
}
@objc
private func updateFps(link:CADisplayLink){
if lastTimeStamp == 0{
lastTimeStamp = link.timestamp
return
}
let delta = link.timestamp - lastTimeStamp
frameCount += 1
//统计 1 秒内 CADisplayLink 被调用的次数
if delta >= 1{
let fps = Double(frameCount) / delta
lastTimeStamp = link.timestamp
frameCount = 0
// 低于 55 FPS 可能意味着轻微卡顿,低于 30 FPS 则可能有严重卡顿
if fps < 65{
print("监测到FPS下降:\(Int(fps))")
//写入到日志系统可以
os_signpost(.event, log: log, name: "FPS Drop", "%d FPS detected", Int(fps))
/*
如何在 Instruments 里查看 os_signpost 日志
1. 打开 Instruments
• 在 Xcode 中,点击 Product -> Profile (⌘ + I) 进入 Instruments。
2. 选择 "Points of Interest"
• 选择 Points of Interest 模板,可以看到 os_signpost 记录的 FPS 下降点。
3. 运行应用
• 运行 App,触发 FPS 下降(比如滚动复杂 UI),查看 Instruments 的数据。
*/
}
}
}
}