ios分析app卡顿问题方案

发布于:2025-03-22 ⋅ 阅读:(146) ⋅ 点赞:(0)

两个方案,一个是监听runloop,一个是监听FPS

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
    }
    
    
    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 的数据。
                 */
            }
        }
    }
}