現在如果在網路上搜的話,基本上大多數用於檢測FPS的控制元件都是通過CADisplayLink
來實現的。
CADisplayLink
官方文件對於CADisplayLink
的介紹是:
A timer object that allows your application to synchronize its drawing to the refresh rate of the display.
即與螢幕重新整理率同步的時間物件。
一般情況下,我們的螢幕重新整理率是1/60s一次。CADisplayLink
實際上跟平常用的NSTimer
的用法基本相似,NSTimer
的時間間隔是以秒為單位,而CADisplayLink
則是使用幀率來作為時間間隔的單位。
利用CADisplayLink
來實現FPS監測的常規做法如下:
var historyCount: Int = 0
var lastUpdateTimestamp: Double = 0
let displayLink = CADisplayLink(target: self, selector: #selector(step(displayLink:))
// ...
func step(displayLink: CADisplayLink) {
if (lastUpdateTimestamp <= 0) {
lastUpdateTimestamp = displayLink.timestamp
}
historyCount += 1
let interval = displayLink.timestamp - lastUpdateTimestamp
if (interval >= 1) {
lastUpdateTimestamp = 0
let fps = Double(historyCount) / interval
print(fps)
historyCount = 0
}
}
複製程式碼
核心思想為:在初始化CADisplayLink
物件時,指定方法,該方法會在每次螢幕重新整理,即每1/60秒呼叫一次,通過計算方法的呼叫次數以及時間間隔,來獲取當前螢幕的fps
測試
根據上面的程式碼,我建立了一個tableView,在cell中各種圓角圖片,反正就是怎麼卡怎麼來:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell!.imageView!.image = UIImage(named: "00" + String(indexPath.row % 8 + 1))
cell!.imageView!.layer.cornerRadius = 10
cell!.imageView!.clipsToBounds = true
cell!.imageView!.layer.shadowOffset = CGSize(width: 0, height: 5)
cell!.imageView!.layer.shadowOpacity = 1
if (indexPath.row % 3 == 0) {
cell!.textLabel!.text = "上路 鞏州遇虎熊五百年前一場瘋騰霄又是孫悟空失馬 鷹愁澗飛白龍沙河阻斷 路難通福陵山中收天"
} else if (indexPath.row % 3 == 1) {
cell!.textLabel!.text = "嶺上 前行逆黃風七星不照 波月洞千年白骨 化陰風魚籃 網通天一尾紅紫金葫蘆二道童九尾老狐敢壓龍白虹墜 雪浪擊石碎思歸 難歸 墮回 輪迴"
} else {
cell!.textLabel!.text = "紅霓垂 九重紫雲飛久歸 未歸 欲回 恨回凡胎恰登對 天命難違比丘走白鹿 十三娘情絲纏縛烏袍君生百目廟前攔路自稱黃眉老祖"
}
cell!.textLabel!.backgroundColor = UIColor.clear
cell!.textLabel!.layer.shadowOffset = CGSize(width: 0, height: 2)
cell!.textLabel!.layer.shadowOpacity = 1
cell!.textLabel!.numberOfLines = 0
return cell!
}
複製程式碼
在執行時可以看到,列印出來的幀率為:
可是通過Instrument的Core Animation進行監測的時候,其結果卻是:
兩者完全就對不上啊。
在這篇文章中,發現作者也遇到相同的問題:iOS中基於CADisplayLink的FPS指示器詳解
根據大神ibireme的文章iOS 保持介面流暢的技巧的介紹,我們能夠知道在螢幕中顯示影象的過程中,CPU負責計算顯示內容,進行諸如檢視建立,佈局計算,圖片解碼等工作,然後將資料提交到GPU上,而GPU對這些影象資料進行變換,渲染之後,會把影象提交到幀緩衝區,然後在下一次同步訊號來臨的時候,將影象顯示到螢幕上。然後GPU就切換指向到另一個幀緩衝區,重複上述工作。
由此可以得知,因為CADisplayLink
的執行取決於RunLoop
。而RunLoop
的執行取決於其所在的mode
以及CPU的繁忙程度,當CPU忙於計算顯示內容或者GPU工作太繁重時,就會導致顯示出來的FPS與Instrument的不一致。
故使用CADisplayLink
並不能很準確反映當前螢幕的FPS!
主執行緒卡頓監測
由於CADisplayLink
並不能夠準確反映出來,所以常用的方法時主執行緒卡頓監測。通過開闢一個子執行緒來監測主執行緒的RunLoop,當兩個狀態區域的耗時大於設定的閾值時,即為一次卡頓。
根據如何監控卡頓的介紹,可以得知主執行緒卡頓監測的原理以及做法。
結論
根據CADisplayLink
寫了一個小工具:KJFPSLabel