iOS-卡頓監(jiān)測-FPS監(jiān)測(附詳細代碼及原理講解)

序言

一個應用的好壞很大程度上取決于使用是否流暢,所以明白造成應用卡頓的原因及解決思路顯的至關重要。

一 檢測的方案根據(jù)線程是否相關分為兩大類:
  • 執(zhí)行耗時任務會導致CPU短時間無法響應其他任務,檢測任務耗時來判斷是否可能導致卡頓

  • 由于卡頓直接表現(xiàn)為操作無響應,界面動畫遲緩,檢測主線程是否能響應任務來判斷是否卡頓

與主線程相關的檢測方案包括:

  • fps
  • ping
  • runloop

與主線程不相關的檢測包括:

  • stack backtrace
  • msgSend observe
二 FPS 監(jiān)測

通常情況下,屏幕會保持60hz/s的刷新速度,每次刷新時會發(fā)出一個屏幕刷新信號,CADisplayLink允許我們注冊一個與刷新信號同步的回調處理。可以通過屏幕刷新機制來展示fps值:

方法一

附核心代碼

@implementation ViewController {
    UILabel *_fpsLbe;
    
    CADisplayLink *_link;
    NSTimeInterval _lastTime;
    float _fps;
}

- (void)startMonitoring {
    if (_link) {
        [_link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
        [_link invalidate];
        _link = nil;
    }
    _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(fpsDisplayLinkAction:)];
    [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)fpsDisplayLinkAction:(CADisplayLink *)link {
    if (_lastTime == 0) {
        _lastTime = link.timestamp;
        return;
    }
    
    self.count++;
    NSTimeInterval delta = link.timestamp - _lastTime;
    if (delta < 1) return;
    _lastTime = link.timestamp;
    _fps = _count / delta;
    NSLog(@"count = %d, delta = %f,_lastTime = %f, _fps = %.0f",_count, delta, _lastTime, _fps);
    self.count = 0;
    _fpsLbe.text = [NSString stringWithFormat:@"FPS:%.0f",_fps];
}

運行效果圖

1.gif

監(jiān)聽count值的改變

#pragma mark - observer

- (void)addObserver {
    [self addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"count new = %@, old = %@",[change valueForKey:@"new"], [change valueForKey:@"old"]);
}
image.png

分析

1.通過打印,我們知道每次刷新時會發(fā)出一個屏幕刷新信號,則與刷新信號同步的回調方法`fpsDisplayLinkAction:`會調用,然后count加一。
2.每隔一秒,我們計算一次 `fps `值,用一個變量_lastTime記錄上一次計算 fps 值的時間,然后將 count 的值除以時間間隔,就得到了 fps 的值,在將_lastTime重新賦值,_count置成零。
3.正常情況下,屏幕會保持60hz/s的刷新速度,所以1秒內`fpsDisplayLinkAction:`方法會調用60次。fps 計算的值為0,就不卡頓,流暢。
4.如果1秒內`fpsDisplayLinkAction:`只回調了50次,計算出來的fps就是 _count / delta(時間間隔) 。
方法二

核心代碼

- (void)startFpsMonitoring {
    _link = [CADisplayLink displayLinkWithTarget: self selector: @selector(displayFps:)];
    [_link addToRunLoop: [NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes];
}

- (void)displayFps: (CADisplayLink *)fpsDisplay {
    self.count++;
    CFAbsoluteTime threshold = CFAbsoluteTimeGetCurrent() - _lastTime;
    if (threshold >= 1.0) {
        _fps = (_count / threshold);
        _lastTime = CFAbsoluteTimeGetCurrent();
        _fpsLbe.text = [NSString stringWithFormat:@"FPS:%.0f",_fps];
        self.count = 0;
        NSLog(@"count = %d,_lastTime = %f, _fps = %.0f",_count, _lastTime, _fps);
    }
}

運行結果和方法一差不多。

結論

image.png

本文參考 質量監(jiān)控-卡頓檢測,感謝該作者。


項目連接地址 - FpsMonitoring

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容