iOS FPS監測

如今若是在網絡上搜的話,基本上大多數用於檢測FPS的控件都是經過CADisplayLink來實現的。ios

CADisplayLink

官方文檔對於CADisplayLink的介紹是:git

A timer object that allows your application to synchronize its drawing to the refresh rate of the display.github

即與屏幕刷新率同步的時間對象。swift

通常狀況下,咱們的屏幕刷新率是1/60s一次。CADisplayLink實際上跟日常用的NSTimer的用法基本類似,NSTimer的時間間隔是以秒爲單位,而CADisplayLink則是使用幀率來做爲時間間隔的單位。網絡

利用CADisplayLink來實現FPS監測的常規作法以下:app

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中各類圓角圖片,反正就是怎麼卡怎麼來:oop

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!
}
複製代碼

在運行時能夠看到,打印出來的幀率爲:佈局

CADisplayLink 幀率

但是經過Instrument的Core Animation進行監測的時候,其結果倒是:測試

Instrument 結果

二者徹底就對不上啊。

在這篇文章中,發現做者也遇到相同的問題:iOS中基於CADisplayLink的FPS指示器詳解

根據大神ibireme的文章iOS 保持界面流暢的技巧的介紹,咱們可以知道在屏幕中顯示圖像的過程當中,CPU負責計算顯示內容,進行諸如視圖建立,佈局計算,圖片解碼等工做,而後將數據提交到GPU上,而GPU對這些圖像數據進行變換,渲染以後,會把圖像提交到幀緩衝區,而後在下一次同步信號來臨的時候,將圖像顯示到屏幕上。而後GPU就切換指向到另外一個幀緩衝區,重複上述工做。

由此能夠得知,由於CADisplayLink的運行取決於RunLoop。而RunLoop的運行取決於其所在的mode以及CPU的繁忙程度,當CPU忙於計算顯示內容或者GPU工做太繁重時,就會致使顯示出來的FPS與Instrument的不一致。

故使用CADisplayLink並不能很準確反映當前屏幕的FPS!

主線程卡頓監測

因爲CADisplayLink並不可以準確反映出來,因此經常使用的方法時主線程卡頓監測。經過開闢一個子線程來監測主線程的RunLoop,當兩個狀態區域的耗時大於設定的閾值時,即爲一次卡頓。

根據如何監控卡頓的介紹,能夠得知主線程卡頓監測的原理以及作法。

結論

根據CADisplayLink寫了一個小工具:KJFPSLabel

相關文章
相關標籤/搜索