当前位置: 首页 > article >正文

ios分析app卡顿问题方案

两个方案,一个是监听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 的数据。
                 */
            }
        }
    }
}


http://www.kler.cn/a/599810.html

相关文章:

  • 以太坊兼容智能合约即将登陆 Kusama!Polkadot 迎来智能合约新时代
  • Java高频面试之集合-17
  • 【C/C++】在排序数组中查找元素的第一个和最后一个位置(leetcode T34)
  • Flutter项目升级到指定版本的详细步骤指南
  • 重要重要!!fisher矩阵是怎么计算和更新的,以及计算过程中参数的物理含义
  • 如何让WordPress不同的页面、栏目显示不同的小工具侧边栏
  • 新版frp-0.61.0 实现泛解析域名穿透 以及 https启用
  • vue3 项目的最新eslint9 + prettier 配置
  • 数据库联表Sql语句建一个新表(MySQL,Postgresql,SQL server)
  • OpenHarmony和HarmonyOS到底有什么区别?
  • 【GL010】C++
  • 哨兵-6 (Sentinel-6)
  • VSCode 出现一直Reactivating terminals,怎么破
  • 数据结构每日一题day1
  • docker模拟Dos_SYN Flood拒绝服务攻击 (Ubuntu20.04)
  • uniapp处理流式请求
  • PLY格式文件如何转换成3DTiles格式——使用GISBox软件实现高效转换
  • [深度学习]特征提取和无监督
  • 精通服务器推送事件(SSE)与 Python 和 Go 实现实时数据流 [特殊字符]
  • Qt6相对Qt5的主要提升(AI总结)