本文意义在分析如何利用runloop监控卡顿。代码可以看戴铭大佬的代码
思路
首先思路就是利用CFRunloopObserverContext监控主线程runloop的状态,通过主线程中任务执行的时间来判断是否有卡顿存在。
CFRunloopObserverContext一共有6种状态
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
//即将进入runloop
kCFRunLoopEntry = (1UL << 0),
//即将处理timer
kCFRunLoopBeforeTimers = (1UL << 1),
//即将处理source
kCFRunLoopBeforeSources = (1UL << 2),
//即将进入休眠
kCFRunLoopBeforeWaiting = (1UL << 5),
//被唤醒但是还开始处理事件
kCFRunLoopAfterWaiting = (1UL << 6),
//runloop已经退出
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
下图所示的是runloop的工作流程和对应的observer。
解释一下其工作过程,在进入runloop之后,当前runloop会不断按照在kCFRunLoopBeforeTimers-> kCFRunLoopBeforeSources-> kCFRunLoopBeforeWaiting-> kCFRunLoopAfterWaiting的顺序循环,直到接收到退出runloop的消息,那么我们只要知道线程任务在哪个状态区间执行的并且抓取这个时间间隔,如果时间间隔超过阈值,则说明卡顿,上报堆栈信息。
由上图可以看出真正处理事务的状态区间分别是:
- KCFRunloopBeforeSources->KCFRunLoopBeforeWaiting
- kCFRunLoopAfterWaiting-> kCFRunLoopBeforeTimers
代码如下:
#import "SMLagMonitor.h"
@interface SMLagMonitor(){
int timeoutCount;
CFRunLoopObserverRef runLoopObserver;
@public
dispatch_semaphore_t dispatchSemaphore;
CFRunLoopActivity runloopActivity;
}
@property (nonatomic, strong) NSTimer * cpuMonitorTimer;
@end
@implementation SMLagMonitor
+ (instancetype)shareInstance{
static id instance = nil;
static dispatch_once_t dispatchOnce;
dispatch_once(&dispatchOnce, ^{
instance = [[self alloc] init];
});
return instance;
}
- (void)beginMonitor{
if (runLoopObserver){
return;
}
dispatchSemaphore = dispatch_semaphore_create(0);
CFRunLoopObserverContext context = {0,(__bridge void *)self,NULL,NULL};
runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runloopObserverCallBack, &context);
CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (YES) {
//dispatch_semaphore_wait方法如果超时则会返回一个不等于0的整数,收到dispatch_semaphore_signal的时候会返回0
long semaphoreWait = dispatch_semaphore_wait(self->dispatchSemaphore, dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC));
if (semaphoreWait != 0){
if (!self->runLoopObserver){
self->timeoutCount = 0;
self->dispatchSemaphore = 0;
self->runloopActivity = 0;
return ;
}
//BeforeSource和AfterWaiting这两个状态区间能够监测到是否卡顿
if (self->runloopActivity == kCFRunLoopBeforeSources || self->runloopActivity == kCFRunLoopAfterWaiting){
NSLog(@"monitor trigger");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//上报堆栈
});
}
}
self->timeoutCount = 0;
}
});
}
static void runloopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void * info){
SMLagMonitor *lagMonitor = (__bridge SMLagMonitor *)info;
lagMonitor->runloopActivity = activity;
dispatch_semaphore_t semaphore = lagMonitor->dispatchSemaphore;
dispatch_semaphore_signal(semaphore);
}
@end