ios分析app卡顿问题方案

两个方案,一个是监听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 的数据。
                 */
            }
        }
    }
}
相关推荐
ssshooter16 小时前
Tauri 踩坑 appLink 修改后闪退
前端·ios·rust
二流小码农19 小时前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
开心就好20251 天前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios
开心就好20252 天前
免 Xcode 的 iOS 开发新选择?聊聊一款更轻量的 iOS 开发 IDE kxapp 快蝎
后端·ios
恋猫de小郭2 天前
Apple 的 ANE 被挖掘,AI 硬件公开,宣传的 38 TOPS 居然是"数字游戏"?
前端·人工智能·ios
忆江南2 天前
iOS 深度解析
flutter·ios
没有故事的Zhang同学2 天前
05-主题|事件响应者链@iOS-应用场景与进阶实践
ios
FeliksLv3 天前
尝试给Lookin 支持 MCP
ios
没有故事的Zhang同学3 天前
01-研究系统框架@Web@iOS | JavaScriptCore 框架:从使用到原理解析
ios
CocoaKier4 天前
苹果谷歌商店:如何监控并维护用户评分评论
ios·google·apple