Runloop的奇技淫巧

前言
相信大家對Runloop都或多或少有過一定的了解,就算沒有使用過Runloop但也應(yīng)該聽說,尤其對于iOS開發(fā)。這篇文章并不會(huì)詳細(xì)講解Runloop的內(nèi)部實(shí)現(xiàn)與工作原理,主要以iOS開發(fā)的角度介紹Runloop的使用實(shí)踐。

什么是Runloop

Runloop簡單來說就是一個(gè)消息循環(huán),如同do{}While,主用用于資源的合理分配,有消息時(shí)喚起Runloop處理消息、沒有消息時(shí)進(jìn)入休眠、合適時(shí)機(jī)切換循環(huán)的mode,避免消息堆積。網(wǎng)上介紹Runloop的文章很多也很詳細(xì),并且蘋果已經(jīng)開源了CoreFoundation源代碼以及Runloop官方文檔,這里也不展開介紹了。

實(shí)踐

1.卡頓監(jiān)控

卡頓在用戶的感知是頁面掉幀、無法操作、操作響應(yīng)不及時(shí),在研發(fā)的角度來看就是主線程正在執(zhí)行耗時(shí)代碼,導(dǎo)致頁面渲染頻率跟不上CPU刷新頻率或主線程無法及時(shí)響應(yīng)用戶交互事件。

  • 通用解決方案
    使用CADisplayLink計(jì)算FPS,通過FPS監(jiān)控卡頓情況,但是這種方案局限較大,無法精確定位導(dǎo)致卡頓的問題代碼。
  • 卡頓的根本原因位
    用戶交互事件的響應(yīng)與頁面渲染幀的計(jì)算在每個(gè)消息循環(huán)內(nèi)都能處理,故如果單個(gè)消息循環(huán)耗時(shí)不長,就不會(huì)導(dǎo)致卡頓的產(chǎn)生
  • Runloop方案
    在子線程通過CFRunLoopObserver監(jiān)聽主線程Runloop的CFRunLoopActivity狀態(tài),根據(jù)kCFRunLoopBeforeSourceskCFRunLoopAfterWaiting兩個(gè)狀態(tài)確定每個(gè)消息循環(huán)耗時(shí)情況,根據(jù)合理的閥值找出有問題的消息循環(huán),并通過當(dāng)時(shí)的函數(shù)調(diào)用堆棧即可獲取問題代碼,可用于統(tǒng)計(jì)卡頓的量與問題分析。

2.預(yù)加載分發(fā)

性能優(yōu)化過程中少不了預(yù)加載、預(yù)計(jì)算等手段,這些手段確實(shí)能給某些指標(biāo)帶來可觀的收益,但是預(yù)加載的時(shí)機(jī)也是個(gè)需要考慮的問題,如果時(shí)機(jī)沒選好或者多個(gè)預(yù)加載堆積可能會(huì)更影響體驗(yàn)。

  • 案例
    TableView的性能優(yōu)化的過程中,我們會(huì)預(yù)計(jì)算為展示cell的布局或cell高度,以保證滑動(dòng)的流程性。常規(guī)做法是在TableView滑停的時(shí)候進(jìn)行預(yù)計(jì)算,但一般用戶滑停后大概率會(huì)進(jìn)行點(diǎn)擊等操作,而預(yù)計(jì)算可能導(dǎo)致用戶無法及時(shí)交互。
  • 空閑分發(fā)
    建立一個(gè)預(yù)加載分發(fā)隊(duì)列,同時(shí)監(jiān)聽主線程Runloop的kCFRunLoopBeforeWaiting狀態(tài),每監(jiān)聽到一次變?yōu)?code>BeforeWaiting的時(shí)機(jī),就將一個(gè)預(yù)加載任務(wù)通過performSelector:onThread:withObject:waitUntilDone:modes:source0的方式拋給主線程Runloop處理。同時(shí)隊(duì)列可提供任務(wù)的優(yōu)先級排序與任務(wù)的撤銷,便于使用方的資源合理分配與預(yù)加載回收。

3.容災(zāi)?;?/h4>

App出現(xiàn)Crash發(fā)生異常奔潰是在所難免的事,雖然我們可以通過加保護(hù)、消息轉(zhuǎn)發(fā)等手段做到一定程度的容災(zāi),但還是無法預(yù)測所有的異常發(fā)生,在異常發(fā)生后希望給用戶一定的提示或者短暫的操作時(shí)間,進(jìn)行數(shù)據(jù)的保持等行為。

  • 異常捕獲
    系統(tǒng)提供了NSSetUncaughtExceptionHandler()signal()用于捕獲nativesignal的異常處理,但受限于只有當(dāng)前ExceptionHandler所在Runloop的執(zhí)行時(shí)間,期間無法給用戶提供操作時(shí)間。
  • ?;?br> 在ExceptionHandler執(zhí)行過程中可通過CFRunLoopRun()CFRunLoopRunInMode()重啟主線程Runloop,臨時(shí)掛起ExceptionHandler的上下文。期間提示用戶且用戶可正常操作,待操作完成后以CFRunLoopStop()關(guān)閉ExceptionHandler中新起的消息循環(huán)并還原ExceptionHandler的上下文,完成異常處理的完整流程。
  • 風(fēng)險(xiǎn)
    1. ExceptionHandler的執(zhí)行時(shí)間是否受Watch Dog的監(jiān)控未知,暫未查到資料。
    2. 異常已經(jīng)觸發(fā),臨時(shí)?;钜部赡苁遣煌暾δ?。
    3. 保活后系統(tǒng)主線程調(diào)用堆棧都是從ExceptionHandler開始的,即堆棧中ExceptionHandler變成正常情況下main函數(shù)的位置。

小結(jié)

以上幾種Runloop的落地方案均只提及思想,未提供具體實(shí)現(xiàn)細(xì)節(jié),主要是因各個(gè)場景下有各自的訴求,即實(shí)現(xiàn)細(xì)節(jié)各有千秋??D監(jiān)控與預(yù)加載分發(fā)方案落地沒有什么異議,至于容災(zāi)?;钍欠窨陕涞乜勺们檫x擇。再者大家有其他更好的Runloop落地方案也歡迎留言補(bǔ)充。

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

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

  • 目錄 Runloop RunLoop 與線程 個(gè)人理解總結(jié) 應(yīng)用場景 1. 什么是RunLoop 基本作用 保持程...
    Ryan___閱讀 1,624評論 0 13
  • RunLoop的概念 一般來講,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完成后線程就會(huì)退出。如果我們需要一個(gè)機(jī)制,讓線程...
    SunZzzl閱讀 285評論 0 1
  • RunLoop的定義與概念RunLoop的主要作用main函數(shù)中的RunLoopRunLoop與線程的關(guān)系RunL...
    __silhouette閱讀 1,077評論 0 6
  • ?xml version="1.0" encoding="UTF-8"? 參考文章:深入理解RunLoop[htt...
    Nomo_C閱讀 388評論 0 1
  • RunLoop 的概念一般來講,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完成后線程就會(huì)退出。如果我們需要一個(gè)機(jī)制,讓線程...
    城市之光閱讀 833評論 0 1

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