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 的数据。
                 */
            }
        }
    }
}
相关推荐
蓝婷儿4 小时前
前端面试每日三题 - Day 30
前端·面试·职场和发展
名字不要太长 像我这样就好8 小时前
【iOS】源码阅读(二)——NSObject的alloc源码
开发语言·macos·ios·objective-c
wang__1230011 小时前
力扣70题解
算法·leetcode·职场和发展
咚咚轩11 小时前
蓝桥杯14届 数三角
蓝桥杯·stl
_Itachi__12 小时前
LeetCode 热题 100 101. 对称二叉树
算法·leetcode·职场和发展
嗨信奥15 小时前
蓝桥杯青少 图形化编程(Scratch)编程题每日一练——小猫的城堡
青少年编程·蓝桥杯
I烟雨云渊T17 小时前
iOS实名认证模块的具体实现过程(swift)
ios·cocoa·swift
小鹿撞出了脑震荡18 小时前
汇编学习——iOS开发对arm64汇编的初步了解
汇编·学习·ios
Kent_J_Truman18 小时前
最长字符串 / STL+BFS
蓝桥杯