序言
一個應用的好壞很大程度上取決于使用是否流暢叶撒,所以明白造成應用卡頓的原因及解決思路顯的至關重要。
一 檢測的方案根據(jù)線程是否相關分為兩大類:
執(zhí)行耗時任務會導致CPU短時間無法響應其他任務,檢測任務耗時來判斷是否可能導致卡頓
由于卡頓直接表現(xiàn)為操作無響應白胀,界面動畫遲緩,檢測主線程是否能響應任務來判斷是否卡頓
與主線程相關的檢測方案包括:
fps
ping
runloop
與主線程不相關的檢測包括:
stack backtrace
msgSend observe
二 FPS 監(jiān)測
通常情況下,屏幕會保持60hz/s
的刷新速度纽甘,每次刷新時會發(fā)出一個屏幕刷新信號
,CADisplayLink
允許我們注冊一個與刷新信號同步的回調(diào)處理抽碌『酚可以通過屏幕刷新機制來展示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ā)出一個屏幕刷新信號爷辙,則與刷新信號同步的回調(diào)方法`fpsDisplayLinkAction:`會調(diào)用,然后count加一褐墅。
2.每隔一秒痴颊,我們計算一次 `fps `值赏迟,用一個變量_lastTime記錄上一次計算 fps 值的時間,然后將 count 的值除以時間間隔蠢棱,就得到了 fps 的值锌杀,在將_lastTime重新賦值,_count置成零泻仙。
3.正常情況下糕再,屏幕會保持60hz/s的刷新速度,所以1秒內(nèi)`fpsDisplayLinkAction:`方法會調(diào)用60次玉转。fps 計算的值為0突想,就不卡頓,流暢冤吨。
4.如果1秒內(nèi)`fpsDisplayLinkAction:`只回調(diào)了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
本文參考 質(zhì)量監(jiān)控-卡頓檢測垒探,感謝該作者。