先来聊聊RunLoop,Runloop是一个接收处理消息的对象,它经过入口函数来启动执行Event loop模型逻辑.RunloopiOS中只能在当前线程中获取,不能手动建立(主线程的RunLoop能够在子线程中获取).获取的时候系统会自动建立.bash
RunLoop的内部结构以下:async
Observer主要用来得知RunLoop不一样时期的状态,当RunLoop状态改变后会通知Observer.咱们获取或者知道这些状态何时触发,能够去作不少操做和优化.函数
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry , // 进入 loop
kCFRunLoopBeforeTimers , // 触发 Timer 回调
kCFRunLoopBeforeSources , // 触发 Source0 回调
kCFRunLoopBeforeWaiting , // 等待 mach_port 消息,进入休眠
kCFRunLoopAfterWaiting ), // 唤醒,接收 mach_port 消息
kCFRunLoopExit , // 退出 loop
kCFRunLoopAllActivities // loop 全部状态改变
}
复制代码
Source是数据源抽象类.它有两个版本:一个是Source0,一个是Source1.其中Source0主要用于处理内部事件,App本身管理的事件,好比:UIEvent、UITouch、CFSockt等.这些Source0是先被标记为待处理,而后再唤醒Runloop处理. Source1是由XNU内核管理,Mach_Port来驱动使用当.触发trap内核会被唤醒,mach_msg()方法会从用户态切换到内核态,内核态中的mach_msg()会完成实际任务.oop
Timer就是定时相关的了,好比NSTimer就会把不一样的时间点注册到RunLoop中,定时唤醒Port,处理消息.优化
下面是经过监控主线程RunLoop监控卡顿的代码,及详细注释:ui
@interface MCXLagMonitor(){
CFRunLoopObserverRef runLoopObserver;
int timeoutCounting;//超时计数
dispatch_semaphore_t dispatchSemaphore; //信号量
CFRunLoopActivity runLoopActivity; //RunLoop的状态
}
@end
@implementation MCXLagMonitor
+ (id)shareInstance {
static id instance = nil;
static dispatch_once_t dispatchOnce;
dispatch_once(&dispatchOnce, ^{
instance = [[self alloc]init];
});
return instance;
}
- (void)beginMonitor{
CFRunLoopObserverContext context = {
0,
(__bridge void *)(self),
NULL,
NULL
}; //context是一个结构体 info参数会传到CFRunLoopObserverCreate的callout的info中.
dispatchSemaphore = dispatch_semaphore_create(0);//建立信号量
runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runLoopObserverCallBack, &context);//参数分别是: 分配空间 状态枚举 是否循环调用observer 优先级 回调函数 结构体
CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);//添加到主线程的RunLoop
dispatch_async(dispatch_get_global_queue(0, 0), ^{//开启监控子线程
while (YES) {//loop
long semphoreWait = dispatch_semaphore_wait(self->dispatchSemaphore, dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC));//信号量为0的时候会等待1秒再执行后面的代码,即1秒为超时时间.若是信号量为0等待超时后该方法就返回非零,不然返回0,也就是说信号量在观察到RunLoop变化的时候会执行callout信号量+1,而后该方法-1返回0,继续执行下面方法.另外,1秒timeout的阻塞过程当中,若是信号量因状态改变增量,就直接返回0执行后面代码.
if (semphoreWait == 0) {
self->timeoutCounting = 0;
}else{
if (!self->runLoopObserver) {
self->dispatchSemaphore = 0;
self->timeoutCounting = 0;
self->runLoopActivity = 0;
}
if (self->runLoopActivity == kCFRunLoopBeforeSources || self->runLoopActivity == kCFRunLoopAfterWaiting) {//RunLoop两个状态,若是触发即将进入source0状态后一直没有进入下一个BeforeWaiting状态,那说明方法执行时间过长. 而后是AfterWaiting也就是即将唤醒状态,若是这个状态持续时间太久,说明调用mach_msg 等待接受mach_port的消息时间过长而没法进入下一状态.他们的表现就是阻塞主线程,形成卡顿,经过监控它们来监控卡顿.
if (++self->timeoutCounting<3) {//超过3s上报堆栈信息 若是以为长的话,能够把上面的时间改为纳秒、毫秒等
continue;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{//再开启一个子线程来上报堆栈信息
NSLog(@"堆栈信息");
});
}
}
}
});
}
- (void)endMonitor{
if (!runLoopObserver) {
return;
}
CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
CFRelease(runLoopObserver);
runLoopObserver = NULL;
}
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
MCXLagMonitor *monitor = (__bridge MCXLagMonitor *)(info);//桥接self
monitor->runLoopActivity = activity;
dispatch_semaphore_signal(monitor->dispatchSemaphore);//信号量+1
}
复制代码