前言
本文的demo代码也会更新到github上。
做这个demo思路来源于微信team的:微信iOS卡顿监控系统。
主要思路:通过监测Runloop的kCFRunLoopAfterWaiting,用一个子线程去检查,一次循环是否时间太长。
其中主要涉及到了runloop的原理。关于整个原理:深入理解RunLoop讲解的比较仔细。
以下就是runloop大概的运行方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/// 1. 通知Observers,即将进入RunLoop /// 此处有Observer会创建AutoreleasePool: _objc_autoreleasePoolPush(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry); do { /// 2. 通知 Observers: 即将触发 Timer 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers); /// 3. 通知 Observers: 即将触发 Source (非基于port的,Source0) 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); /// 4. 触发 Source0 (非基于port的) 回调。 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0); /// 5. GCD处理main block __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); /// 6. 通知Observers,即将进入休眠 /// 此处有Observer释放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting); /// 7. sleep to wait msg. mach_msg() -> mach_msg_trap(); /// 8. 通知Observers,线程被唤醒 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting); /// 9. 如果是被Timer唤醒的,回调Timer __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer); /// 9. 如果是被dispatch唤醒的,执行所有调用 dispatch_async 等方法放入main queue 的 block __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block); /// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件唤醒了,处理这个事件 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1); } while (...); /// 10. 通知Observers,即将退出RunLoop /// 此处有Observer释放AutoreleasePool: _objc_autoreleasePoolPop(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit); } |
其中UI主要集中在__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
和__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
之前。
获取kCFRunLoopBeforeSources
到kCFRunLoopBeforeWaiting
再到kCFRunLoopAfterWaiting
的状态就可以知道是否有卡顿的情况。
NSTimer的实现
具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// // MonitorController.h // RunloopMonitorDemo // // Created by game3108 on 16/4/13. // Copyright © 2016年 game3108. All rights reserved. // #import @interface MonitorController : NSObject + (instancetype) sharedInstance; - (void) startMonitor; - (void) endMonitor; - (void) printLogTrace; @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
// // MonitorController.m // RunloopMonitorDemo // // Created by game3108 on 16/4/13. // Copyright © 2016年 game3108. All rights reserved. // #import "MonitorController.h" #include #include @interface MonitorController(){ CFRunLoopObserverRef _observer; double _lastRecordTime; NSMutableArray *_backtrace; } @end @implementation MonitorController static double _waitStartTime; + (instancetype) sharedInstance{ static dispatch_once_t once; static id sharedInstance; dispatch_once(&once, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; } - (void) startMonitor{ [self addMainThreadObserver]; [self addSecondaryThreadAndObserver]; } - (void) endMonitor{ if (!_observer) { return; } CFRunLoopRemoveObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes); CFRelease(_observer); _observer = NULL; } #pragma mark printLogTrace - (void)printLogTrace{ NSLog(@"====================堆栈\n %@ \n",_backtrace); } #pragma mark addMainThreadObserver - (void) addMainThreadObserver { dispatch_async(dispatch_get_main_queue(), ^{ //建立自动释放池 @autoreleasepool { //获得当前thread的Run loop NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop]; //设置Run loop observer的运行环境 CFRunLoopObserverContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; //创建Run loop observer对象 //第一个参数用于分配observer对象的内存 //第二个参数用以设置observer所要关注的事件,详见回调函数myRunLoopObserver中注释 //第三个参数用于标识该observer是在第一次进入run loop时执行还是每次进入run loop处理时均执行 //第四个参数用于设置该observer的优先级 //第五个参数用于设置该observer的回调函数 //第六个参数用于设置该observer的运行环境 _observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context); if (_observer) { //将Cocoa的NSRunLoop类型转换成Core Foundation的CFRunLoopRef类型 CFRunLoopRef cfRunLoop = [myRunLoop getCFRunLoop]; //将新建的observer加入到当前thread的run loop CFRunLoopAddObserver(cfRunLoop, _observer, kCFRunLoopDefaultMode); |