首先本文的思路来自于网上的各种资料。然后搜了半天发现没有swift版的,于是撸了一个。
其实具体的思路非常的简单:
-
首先创建一个runloop的observer对象:
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中
CFRunLoopAddObserver(CFRunLoopGetCurrent(), self.runLoopObserver, CFRunLoopMode.commonModes)
ps: 因为common modes是会一直存在于runloop中的,不会被中断,所以将检测的observer对象放到这个modes里去。
-
检测CFRunLoopActivity
CFRunLoopActivity这个结构有非常多的状态吧,我们需要判断的是:
beforeSources: 进入睡眠前 afterWaiting: 唤醒后的状态
如果runloop返回的activity的值是上述的两个,那么就可以认为出现了卡顿的现象
-
这里用了dispatch的信号机制
self.dispatchSemaphore?.wait(timeout: DispatchTime.now() + 0.05)
这段代码认定,如果连五次次超过了0.088秒,那么就认为发生了卡顿的现象
实现的逻辑就是这么四步,下面贴上全部的代码:
import Foundation
private let START_MONITOR_RATE = 88
class DJMonitor {
static let shared = DJMonitor()
var isMoniting = false
var timeoutCount = 0
var runLoopActivity: CFRunLoopActivity = .entry
var dispatchSemaphore: DispatchSemaphore? = DispatchSemaphore(value: 0)
var runloopObserver: CFRunLoopObserver?
func start() {
guard !isMoniting else {
return
}
self.runloopObserver = buildRunLoopObserver()
if self.runloopObserver == nil {
print("创建监听失败...")
return
}
isMoniting = true
CFRunLoopAddObserver(CFRunLoopGetMain(), runloopObserver, CFRunLoopMode.commonModes)
DispatchQueue.global().async {
while true {
let wait = self.dispatchSemaphore?.wait(timeout: DispatchTime.now() + 0.05)
if DispatchTimeoutResult.timedOut == wait {
guard self.runloopObserver != nil else {
self.dispatchSemaphore = nil
self.runLoopActivity = .entry
return
}
if self.runLoopActivity == .beforeSources || self.runLoopActivity == .afterWaiting {
if self.timeoutCount < 5 {
self.timeoutCount += 1
continue
}
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
print("check the cpu info...")
}
}
}
self.timeoutCount = 0
}
}
}
func end() {
guard self.runloopObserver != nil else {
return
}
self.isMoniting = false
CFRunLoopRemoveObserver(CFRunLoopGetMain(), self.runloopObserver, CFRunLoopMode.commonModes)
self.runloopObserver = nil
}
private func buildRunLoopObserver() -> CFRunLoopObserver? {
let info = Unmanaged<DJMonitor>.passUnretained(self).toOpaque()
var context = CFRunLoopObserverContext(version: 0, info: info, retain: nil, release: nil, copyDescription: nil)
let observer = CFRunLoopObserverCreate(kCFAllocatorDefault, CFRunLoopActivity.allActivities.rawValue, true, 0, runLoopObserverCallback(), &context)
return observer
}
func runLoopObserverCallback() -> CFRunLoopObserverCallBack {
return { observer, activity, info in
guard let info = info else {
return
}
let weakSelf = Unmanaged<DJMonitor>.fromOpaque(info).takeUnretainedValue()
weakSelf.runLoopActivity = activity
weakSelf.dispatchSemaphore?.signal()
}
}
}