序言
一個應用的好壞很大程度上取決于使用是否流暢,所以明白造成應用卡頓的原因及解決思路顯的至關重要。
一 檢測的方案根據(jù)線程是否相關分為兩大類:
執(zhí)行耗時任務會導致CPU短時間無法響應其他任務,檢測任務耗時來判斷是否可能導致卡頓
由于卡頓直接表現(xiàn)為操作無響應,界面動畫遲緩,檢測主線程是否能響應任務來判斷是否卡頓
與主線程相關的檢測方案包括:
fpspingrunloop
與主線程不相關的檢測包括:
stack backtracemsgSend 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)控-卡頓檢測,感謝該作者。