卡頓問題,就是在主線程上無法響應用戶交互的問題。如果一個App時不時地就給你卡 一下,有時還長時間無響應,這時你還願意繼續用它嗎?所以說,卡頓問題對App的傷 害是巨大的,也是我們必須要重點解決的一個問題。
現在,我們先來看一下導致卡頓問題的幾種原因:
●復雜UI、圖文混排的繪制量過大;
●在主線程.上做網絡同步請求;
●在主線程做大量的I0操作;
●運算量過大,CPU持續高占用;
●死鎖和主子線程搶鎖。
那么,我們如何監控到什么時候會出現卡頓呢?是要監視FPS嗎?FPS是一秒顯示的幀數,也就是-秒內畫面變化數量。如果按照動畫片來說,動畫片的FPS就是24,是達不到60滿幀的。也就是說,對於動畫片來說,24幀時雖然沒有60
幀時流暢,但也已經是連貫的了,所以並不能說24幀時就算是卡住了。 由此可見,簡單地通過監視FPS是很難確定是否會出現卡頓問題了,所以我就果斷棄了誦過監視FPS來監控卡頓的方案
通過RunLoop檢測卡頓
對於iOS開發來說,監控卡頓就是要去找到主線程上都做了哪些事兒。我們都知道,線程的消息事件是依賴於NSRunL oop的,所以從NSRunLoop入手,就可以知道主線程上都調用了哪些方法。我們通過監聽NSRunL oop的狀態,就能夠發現調用方法是否執行時間過長,從而判斷出是否會出現卡頓。所以,我推薦的監控卡頓的方案是:通過監控RunLoop的狀態來判斷是否會出現卡頓。RunLoop是iOs開發中的一個基礎概念,為了幫助你理解並用好這個對象,接下來我會先和你介紹一下它可以做哪些事兒,以及它為什么可以做成這些事兒。RunLoop這個對象,在ios里由CFRunLoop實現。簡單來說,RunL oop是用來監聽輸入源,進行調度處理的。這里的輸入源可以是輸入設備、網絡、周期性或者延遲時間、異步回調。RunL oop會接收兩種類型的輸入源: -種是來自另一個線程或者來自不同應用的異步消息;另一種是來自預訂時間或者重復間隔的同步事件。RunLoop的目的是,當有事件要去處理時保持線程忙,當沒有事件要處理時讓線程進入休眠。所以,了解RunL oop原理不光能夠運用到監控卡頓.上,還可以提高用戶的交互體驗。通過將那些繁重而不緊急會大量占用CPU的任務(比如圖片加載), 放到空閑的RunLoop模式里執行,就可以避開在UITrackingRunL oopMode這個RunL _oop模式時是執行。UITrackingRunL oopMode是用戶進行滾動操作時會切換到的RunL oop模式,避免在這個RunL _oop模式執行繁重的CPU任務,就能避免影響用戶交互操作上體驗。
class func checkfps(){ var runLoopActivity:CFRunLoopActivity = .allActivities let runLoopObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.allActivities.rawValue, true, CFIndex.init(bitPattern: 2000000 - 1)) { (Observer, Activity) in runLoopActivity = Activity var timeoutCount = Date().timeIntervalSince1970 if (runLoopActivity == .beforeWaiting) { timeoutCount = Date().timeIntervalSince1970 } if (runLoopActivity == .afterWaiting) { let new = Date().timeIntervalSince1970 //100毫秒 if new - timeoutCount > 0.01{ let symb = Thread.callStackSymbols print("---------3s-----------\(symb)") } } } CFRunLoopAddObserver(CFRunLoopGetCurrent(), runLoopObserver, CFRunLoopMode.commonModes) }