RunLoop下的卡頓監(jiān)控

在開(kāi)發(fā)中,我們可以使用Xcode自帶的Instruments工具的Core Animation來(lái)對(duì)APP運(yùn)行流暢度進(jìn)行監(jiān)控,使用FPS這個(gè)值來(lái)衡量。這個(gè)工具我們只能知道哪個(gè)界面會(huì)有卡頓,無(wú)法知道到底是什么操作哪個(gè)函數(shù)導(dǎo)致的卡頓。

屏幕快照 2016-12-06 下午10.09.40.png

界面出現(xiàn)卡頓,一般是下面幾種原因:

  • 主線(xiàn)程做大量計(jì)算
  • 主線(xiàn)程大量的I/O操作
  • 大量的UI繪制
  • 主線(xiàn)程進(jìn)行網(wǎng)絡(luò)請(qǐng)求以及數(shù)據(jù)處理
  • 離屏渲染

監(jiān)控界面卡頓,主要是監(jiān)控主線(xiàn)程做了哪些耗時(shí)的操作,之前的文章中已經(jīng)分析過(guò),iOS中線(xiàn)程的事件處理依靠的是RunLoop,正常FPS值為60,如果單次RunLoop運(yùn)行循環(huán)的事件超過(guò)16ms,就會(huì)使得FPS值低于60,如果耗時(shí)更多,就會(huì)有明顯的卡頓。

正常RunLoop運(yùn)行循環(huán)一次的流程是這樣的:

SetupThisRunLoopRunTimeOutTimer();
do {
        __CFRunLoopDoObservers(kCFRunLoopBeforeTimers);
        __CFRunLoopDoObservers(kCFRunLoopBeforeSources);
  
        __CFRunLoopDoBlocks();
        __CFRunLoopDoSource0(); // 處理source0事件,UIEvent事件,比如觸屏點(diǎn)擊

        CheckIfExitMessagesInMainDispatchQueue(); // 檢查是否有分配到主隊(duì)列中的任務(wù)

        __CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);
        var wakeUpPort = SleepAndWaitForWakingUpPorts(); // 開(kāi)始休眠,等待ma ch_msg事件
        
        // mach_msg_trap
        // ZZz.....   sleep
        // Received mach_msg,  wake up
        
        __CFRunLoopDoObservers(kCFRunLoopAfterWaiting); // 被事件喚醒
        // Handle msgs
        if (wakeUpPort == timePort) { // 被喚醒的事件是timer
              __CFRunLoopDoTimers(); 
        } else if (wakePort == mainDispatchQueuePort) { // 主隊(duì)列有調(diào)度任務(wù)
              __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__();
        } else { // source1事件,UI刷新,動(dòng)畫(huà)顯示
              __CFRunLoopDoSource1();
        }
        __CFRunLoopDoBlocks();
} while (!stop && !timeout)

從這個(gè)運(yùn)行循環(huán)中可以看出,RunLoop休眠的事件是無(wú)法衡量的,處理事件的部分主要是在kCFRunLoopBeforeSources之后到kCFRunLoopBeforeWaiting之前kCFRunLoopAfterWaiting 之后和運(yùn)行循環(huán)結(jié)束之前這兩個(gè)部分

監(jiān)控這兩個(gè)部分的耗時(shí),使用CFRunLoopObserverRef來(lái)監(jiān)控RunLoop的狀態(tài):

創(chuàng)建信號(hào)量&獲取Runloop的狀態(tài)

使用信號(hào)量dispatch_semaphore來(lái)控制對(duì)RunLoop狀態(tài)判斷的節(jié)奏,這個(gè)可以保證,每個(gè)RunLoop狀態(tài)的判斷都會(huì)進(jìn)行。
對(duì)RunLoop狀態(tài)的判斷,我們專(zhuān)門(mén)在另外一個(gè)線(xiàn)程做判斷。

需要注意的是,對(duì)卡頓的判斷是通過(guò)kCFRunLoopBeforeSources或者kCFRunLoopBeforeWaiting這兩個(gè)狀態(tài)開(kāi)始后,信號(hào)量+1,這時(shí)候信號(hào)量>0,dispatch_semaphore_wait不會(huì)阻塞,返回0,進(jìn)行下一個(gè)while循環(huán),如果此時(shí)還沒(méi)有進(jìn)入下一個(gè)RunLoop狀態(tài),此時(shí)信號(hào)量=0,dispatch_semaphore_wait就會(huì)在這里阻塞,到了設(shè)定的超時(shí)時(shí)間,dispatch_semaphore_wait的返回值>0,這時(shí)候就會(huì)進(jìn)行耗時(shí)的判斷。
我們可以自己設(shè)定超時(shí)時(shí)間和超過(guò)多少次算卡頓,這里設(shè)置超過(guò)250ms。

子線(xiàn)程監(jiān)控
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容