首先本文的思路來自於網上的各類資料。而後搜了半天發現沒有swift版的,因而擼了一個。swift
其實具體的思路很是的簡單:async
首先建立一個runloop的observer對象:oop
let info = Unmanaged<Monitor>.passUnretained(self).toOpaque()
var context: CFRunLoopObserverContext = CFRunLoopObserverContext(version: 0, info: info, retain: nil, release: nil, copyDescription: nil)
self.runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, CFRunLoopActivity.allActivities.rawValue, true, 0, runLoopCallBack(), &context)
複製代碼
而後將這個觀察對象添加到runloop的common modes中spa
CFRunLoopAddObserver(CFRunLoopGetCurrent(), self.runLoopObserver, CFRunLoopMode.commonModes)
複製代碼
ps: 由於common modes是會一直存在於runloop中的,不會被中斷,因此講檢測的observer對象放到這個modes裏去。code
檢測CFRunLoopActivityserver
CFRunLoopActivity這個結構有很是多的狀態吧,咱們須要判斷的是:對象
beforeSources: 進入睡眠前
afterWaiting: 喚醒後的狀態
複製代碼
若是runloop返回的activity的值是上述的兩個,那麼就能夠認爲出現了卡頓的現象ip
這裏用了dispatch的信號機制it
self.dispatchSemaphore?.wait(timeout: DispatchTime.now() + 1 / 50)io
這段代碼認定,若是每秒的幀數少於50,那麼就認爲發生了卡頓的現象
實現的邏輯就是這麼四步,下面貼上所有的代碼:
import Foundation
class Monitor {
static let shared = Monitor()
private var runLoopObserver: CFRunLoopObserver?
private var dispatchSemaphore: DispatchSemaphore?
private var runLoopActivity: CFRunLoopActivity?
init() {}
func beginMonitor() {
guard self.runLoopObserver == nil else { return }
self.dispatchSemaphore = DispatchSemaphore(value: 0)
let info = Unmanaged<Monitor>.passUnretained(self).toOpaque()
var context: CFRunLoopObserverContext = CFRunLoopObserverContext(version: 0, info: info, retain: nil, release: nil, copyDescription: nil)
self.runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, CFRunLoopActivity.allActivities.rawValue, true, 0, runLoopCallBack(), &context)
CFRunLoopAddObserver(CFRunLoopGetCurrent(), self.runLoopObserver, CFRunLoopMode.commonModes)
DispatchQueue.global().async {
// 若是少於50每幀, 則認爲卡頓
while true {
guard let sem = self.dispatchSemaphore?.wait(timeout: DispatchTime.now() + 1 / 50) else { return }
if case DispatchTimeoutResult.timedOut = sem {
guard let _ = self.runLoopObserver else {
self.dispatchSemaphore = nil
self.runLoopActivity = nil
return
}
// beforeSources: 進入睡眠前
// afterWaiting: 喚醒後的狀態
if (self.runLoopActivity == CFRunLoopActivity.beforeSources || self.runLoopActivity == CFRunLoopActivity.afterWaiting) {
print("symbo: \(Thread.callStackSymbols)")
print("打印卡頓堆棧...")
}
}
}
}
}
func endMonitor() {
if self.runLoopObserver != nil {
return
}
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), self.runLoopObserver, CFRunLoopMode.commonModes)
self.runLoopObserver = nil
}
}
extension Monitor {
func runLoopCallBack() -> CFRunLoopObserverCallBack {
return { (observer, activity, context) -> Void in
let weakSelf = Unmanaged<Monitor>.fromOpaque(context!).takeUnretainedValue()
weakSelf.runLoopActivity = activity
weakSelf.dispatchSemaphore?.signal()
}
}
}複製代碼