ios 开发

iOS卡顿优化

2023-02-25  本文已影响0人  iOS小洁

CPU,GPU

CPU(Central Processing Unit,中央处理器)

对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)

GPU(Graphics Processing Unit,图形处理器)

纹理的渲染

屏幕成像原理

CPU计算-->GPU渲染-->帧缓存-->视频控制器 --> 屏幕

在iOS中是双缓冲机制,有前帧缓存、后帧缓存

[图片上传失败...(image-252a7c-1677419325833)]

卡顿产生原因

CPU、GPU处理一帧画面需要的时间太长,垂直同步信号到来的时候没有内容可以显示

卡顿解决的主要思路

尽可能减少CPU、GPU资源消耗

按照60FPS的刷帧率,每隔16ms就会有一次VSync信号

卡顿优化-CPU

卡顿优化-GPU

离屏渲染

在OpenGL中,GPU有2种渲染方式:

离屏渲染消耗性能的原因:

哪些操作会触发离屏渲染?

卡顿检测

1、PFS计算

创建一个CADisplayLink

_link = [CADisplayLink displayLinkWithTarget:[XZWeakProxy proxyWithTarget:self] selector:@selector(tick:)];
[_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

计算

- (void)tick:(CADisplayLink *)link {
    if (_lastTime == 0) {
        _lastTime = link.timestamp;
        return;
    }

    _count++;
    NSTimeInterval delta = link.timestamp - _lastTime;
    if (delta < 1) return;
    _lastTime = link.timestamp;
    float fps = _count / delta;
    _count = 0;
}

link.timestamp是记录帧当前时间。这里是统计每秒帧数,肉眼可以看到变化
link.duration是每帧时间,1/link.duration是实时帧率。肉眼不容易发现变化

2、RunLoop

可以添加Observer到主线程RunLoop中,通过监听RunLoop状态切换的耗时,以达到监控卡顿的目的

通过子线程监测主线程的RunLoop,判断两个状态(kCFRunLoopBeforeSourceskCFRunLoopAfterWaiting)之间的耗时是否达到一定阈值

import UIKit

class XZBlockMonitor: NSObject {
    
    static let share = XZBlockMonitor.init()
    
    fileprivate var semaphore: DispatchSemaphore!
    fileprivate var timeoutCount: Int!
    fileprivate var activity: CFRunLoopActivity!
    
    private override init() {
        super.init()
    }

    
    public func start(){
        //监控两个状态
        registerObserver()
        
        //启动监控
        startMonitor()
    }
    
    private func registerObserver(){
        let controllerPointer = Unmanaged<XZBlockMonitor>.passUnretained(self).toOpaque()
        var context: CFRunLoopObserverContext = CFRunLoopObserverContext(version: 0, info: controllerPointer, retain: nil, release: nil, copyDescription: nil)
        let observer: CFRunLoopObserver = CFRunLoopObserverCreate(nil, CFRunLoopActivity.allActivities.rawValue, true, 0, { (observer, activity, info) in
            
            guard info != nil else{
                return
            }
            
            let monitor: XZBlockMonitor = Unmanaged<XZBlockMonitor>.fromOpaque(info!).takeUnretainedValue()
            monitor.activity = activity
            let sem: DispatchSemaphore = monitor.semaphore
            sem.signal()
            
        }, &context)
        
        CFRunLoopAddObserver(CFRunLoopGetMain(), observer, CFRunLoopMode.commonModes)
    }
    
    private func  startMonitor(){
        //创建信号
        semaphore = DispatchSemaphore(value: 0)
        //在子线程监控时长
        DispatchQueue.global().async {
            while(true){
                // 超时时间是 1 秒,没有等到信号量,st 就不等于 0, RunLoop 所有的任务
                let st = self.semaphore.wait(timeout: DispatchTime.now()+1.0)
                if st != DispatchTimeoutResult.success {
                    //监听两种状态kCFRunLoopBeforeSources 、kCFRunLoopAfterWaiting,
                    if self.activity == CFRunLoopActivity.beforeSources || self.activity == CFRunLoopActivity.afterWaiting {
                        
                        self.timeoutCount += 1
                        
                        if self.timeoutCount < 2 {
                            print("timeOutCount = \(self.timeoutCount)")
                            continue
                        }
                        // 一秒左右的衡量尺度 很大可能性连续来 避免大规模打印!
                        print("检测到超过两次连续卡顿")
                    }
                }
                self.timeoutCount = 0
            }
        }
    }
}

三方库

Swift

OC

上一篇 下一篇

猜你喜欢

热点阅读