static CFMutableDictionaryRef __CFRunLoops = NULL;
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
//如果__CFRunLoops存放loop的字典为NULL会初始化一个字典并且根据主线程创建mainLoop.
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
//把dict里面元素赋值给__CFRunLoops
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); //根绝线程从字典中取出loop
__CFUnlock(&loopsLock);
if (!loop) {
//如果从字典中取出的loop为NULL就创建一个newLoop
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//不知道为啥再从字典中根据线程取一遍loop上面不是已经取过了没有取到吗?
//这次肯定也是取不到的啊,取不到就把newLoop存到字典中
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
CFRunLoopRef loop = NULL;
CFRunLoopModeRef rlm;
uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
if (NULL == loop) {
return NULL;
}
(void)__CFRunLoopPushPerRunData(loop);
__CFRunLoopLockInit(&loop->_lock);
loop->_wakeUpPort = __CFPortAllocate();
if (CFPORT_NULL == loop->_wakeUpPort) HALT;
__CFRunLoopSetIgnoreWakeUps(loop);
loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
loop->_commonModeItems = NULL;
loop->_currentMode = NULL;
loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
loop->_blocks_head = NULL;
loop->_blocks_tail = NULL;
loop->_counterpart = NULL;
loop->_pthread = t;
#if DEPLOYMENT_TARGET_WINDOWS
loop->_winthread = GetCurrentThreadId();
#else
loop->_winthread = 0;
#endif
rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
return loop;
}
__CFRunLoops是一个字典已线程作为key CFRunLoopRef作为value
从以上代码分析可以看到runloop和线程的关系是一一对应的,但是子线程中的runloop默认是不存在的的 主动在子线程中调用[NSRunLoop currentRunLoop],子线程的runloop会自动创建,主线程的runloop会在__CFRunLoopCreate(pthread_t t)这个函数第一次被调用的时候创建
runloop结构
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
runloop主要结构
struct __CFRunLoop {
CFMutableSetRef _commonModes; // Set
CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
CFRunLoopModeRef _currentMode; // Current Runloop Mode
CFMutableSetRef _modes; // Set里面存了若干个CFRunLoopModeRef
...
};
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
CFMutableSetRef _sources0; // Set CFRunLoopSourceRef
CFMutableSetRef _sources1; // Set CFRunLoopSourceRef
CFMutableArrayRef _observers; // Array CFRunLoopObserverRef
CFMutableArrayRef _timers; // Array 存CFRunLoopTimerRef
...
};
CFRunLoopModeRef代表RunLoop的运行模式
一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
RunLoop启动时只能选择其中一个Mode,作为currentMode
如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
CFRunLoopSourceRef 是事件产生的地方。Source有两个版本:Source0 和 Source1。
• Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
• Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,其原理在下面会讲到。
CFRunLoopTimerRef 是基于时间的触发器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。
CFRunLoopObserverRef是观察者,每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
__CFRunLoop有个概念叫 “CommonModes”:一个 Mode 可以将自己标记为”Common”属性(通过将其 ModeName 添加到 RunLoop 的 “commonModes” 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 “Common” 标记的所有Mode里。
NSLog(@"runloop111 = %@",[NSRunLoop currentRunLoop]);
NSLog(@"===============================");
NSTimer *timer = [NSTimer timerWithTimeInterval:2 block:^(NSTimer * _Nonnull timer) {
NSLog(@"%@",@"111111");
} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[timer fire];
NSLog(@"runloop222 = %@",[NSRunLoop currentRunLoop]);
NSLog(@"===============================");
我们这样先打印runloop 然后再吧timer按照NSRunLoopCommonModes的方式加到NSRunLoop中再打印runloop对比
我们发现timer加到runloop之后 _commonModeItems中首先会有这个timer 并且会吧_commonModeItems中所有的item同步到 具有CommonModes标记的真正的Mode
应用场景举例:主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为”Common”属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。
有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。
runloop源码的运行流程分析
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
// 通知Observers进入loop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 具体要做的事情
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Observers退出loop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
__CFRunLoopUnsetIgnoreWakeUps(rl);
//将要处理timer
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//将要处理Sources
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//处理block
__CFRunLoopDoBlocks(rl, rlm);
//处理Sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
//根绝处理Sources0的结果 来处理block
__CFRunLoopDoBlocks(rl, rlm);
}
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
msg = (mach_msg_header_t *)msg_buffer;
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
//如果有Sources1 跳转到handle_msg
goto handle_msg;
}
}
didDispatchPortLastTime = false;
//通知Observers即将休眠
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
// do not do any user callouts after this point (after notifying of sleeping)
// Must push the local-to-this-activation ports in on every loop
// iteration, as this mode could be run re-entrantly and we don't
// want these ports to get serviced.
__CFPortSetInsert(dispatchPort, waitSet);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
do {
if (kCFUseCollectableAllocator) {
// objc_clear_stack(0);
// <rdar://problem/16393959>
memset(msg_buffer, 0, sizeof(msg_buffer));
}
msg = (mach_msg_header_t *)msg_buffer;
//等待别的消息来唤醒线程
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
// Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
if (rlm->_timerFired) {
// Leave livePort as the queue port, and service timers below
rlm->_timerFired = false;
break;
} else {
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
}
} else {
// Go ahead and leave the inner loop.
break;
}
} while (1);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
// Must remove the local-to-this-activation ports in on every loop
// iteration, as this mode could be run re-entrantly and we don't
// want these ports to get serviced. Also, we don't want them left
// in there if this function returns.
__CFPortSetRemove(dispatchPort, waitSet);
__CFRunLoopSetIgnoreWakeUps(rl);
// user callouts now OK again 结束睡觉
__CFRunLoopUnsetSleeping(rl);
//通知DoObservers结束休眠
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
//这里可能是是 有Sources1直接过来的
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
//被timer执行
__CFArmNextTimerInMode(rlm, rl);
}
else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
//被timer执行
__CFArmNextTimerInMode(rlm, rl);
}
else if (livePort == dispatchPort) {
//被GCD唤醒
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else {
CFRUNLOOP_WAKEUP_FOR_SOURCE();
//处理Source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
//处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished;
}
voucher_mach_msg_revert(voucherState);
os_release(voucherCopy);
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
1通知Observers进入loop
2通知Observers即将处理Timers
3通知Observers即将处理Sources
4处理Blocks
5处理Sources0(根据返回结果 可能再次处理Blocks)
6如果存在Sources1,直接到第8部
7通知Observers开始休眠(等待消息唤醒)
8通知Observers结束休眠(被某个消息唤醒)
---1处理Timer
---2处理GCD Async To Main Queue
---1处理Sources1
9处理Blocks
10根据前面的执行结果决定如何操作
---回到第二步
---退出Loop
11通知Observers退出Loop
Source0 触摸事件处理 performSelector:onThread:
Source1 基于Port的线程间通信 系统事件捕捉
Timers NSTimer performSelector:withObject:afterDelay:
Observers 用于监听RunLoop的状态 UI刷 (BeforeWaiting)
Autorelease pool(BeforeWaiting)
runloop的使用场景
1线程保活
#import "LCPermenantThread.h"
#import "LCThread.h"
@interface LCPermenantThread ()
@property (nonatomic, strong) LCThread *thread;
@property (nonatomic, assign, getter=isStopped) BOOL stopped;
@end
@implementation LCPermenantThread
- (instancetype)init{
if (self = [super init]) {
self.stopped = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[LCThread alloc]initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStopped) {
NSLog(@"while+++");
//这里面不要用strongSelf 因为 runloop会卡在这里用strongSelf就持有了self 必须手动stop runloop
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
NSLog(@"while---");
}
NSLog(@"thread end");
}];
}
return self;
}
- (void)run{
if (!self.thread) {
return;
}
[self.thread start];
}
- (void)executeTask:(LCPermenantThreadTask)task{
if (!self.thread || !task) {
return;
}
[self performSelector:@selector(__executeTask:) onThread:self.thread withObject:task waitUntilDone:NO];
}
- (void)stop{
if (!self.thread) {
return;
}
[self performSelector:@selector(__stop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)__executeTask:(LCPermenantThreadTask)task{
task();
}
- (void)__stop{
self.stopped = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread = nil;
}
- (void)dealloc{
NSLog(@"%s",__func__);
[self stop];
}
@end
2NSTimer注意事项
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 block:^(NSTimer * _Nonnull timer) {
} repeats:YES];
以scheduledTimer开的头创建的的timer默认被加到了当前的runloop的default mode (schedules it on the current run loop in the default mode.)
NSTimer *timer = [[NSTimer alloc] initWithFireDate:nil interval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
}];
NSTimer *timer = [NSTimer timerWithTimeInterval:1 block:^(NSTimer * _Nonnull timer) {
} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]
在timer fire之前必须把timer加入到runloop中 常用到的mode NSDefaultRunLoopMode UITrackingRunLoopMode
需要的注意的是NSRunLoopCommonModes
并不是一种真正的mode 我们吧timer以这样"模式"加到runloop中(不记得Runloop的结构往上面找找)其实是加在了_commonModeItems
里面了. 然后会查看_commonModes里的标记的mode(里面存的是"UITrackingRunLoopMode",和"kCFRunLoopDefaultMode"两个字符串),然后把timer从_commonModeItems复制到标记的Mode中的_timers数组中, 所以 UITrackingRunLoopMode模式下的mode和kCFRunLoopDefaultMode模式下都可以执行timer了
3卡顿检测
下面是检查卡顿的代码利用 监听runloop中Observer发出不同的LoopActivity. 利用信号量 超时过滤出 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting. 因为大部分的操作就是在Sources0和Sources1中处理的 上面这两个状态就是处理Sources0和Sources1之前的状态.
#import "LXDAppFluecyMonitor.h"
#import "LXDBacktraceLogger.h"
#define LXD_DEPRECATED_POLLUTE_MAIN_QUEUE
@interface LXDAppFluecyMonitor ()
@property (nonatomic, assign) int timeOut;
@property (nonatomic, assign) BOOL isMonitoring;
@property (nonatomic, assign) CFRunLoopObserverRef observer;
@property (nonatomic, assign) CFRunLoopActivity currentActivity;
@property (nonatomic, strong) dispatch_semaphore_t semphore;
@property (nonatomic, strong) dispatch_semaphore_t eventSemphore;
@end
#define LXD_SEMPHORE_SUCCESS 0
static NSTimeInterval lxd_restore_interval = 5;
static NSTimeInterval lxd_time_out_interval = 1;
static int64_t lxd_wait_interval = 200 * NSEC_PER_MSEC;
/*!
* @brief 监听runloop状态为before waiting状态下是否卡顿
*/
static inline dispatch_queue_t lxd_event_monitor_queue() {
static dispatch_queue_t lxd_event_monitor_queue;
static dispatch_once_t once;
dispatch_once(&once, ^{
lxd_event_monitor_queue = dispatch_queue_create("com.sindrilin.lxd_event_monitor_queue", NULL);
});
return lxd_event_monitor_queue;
}
/*!
* @brief 监听runloop状态在after waiting和before sources之间
*/
static inline dispatch_queue_t lxd_fluecy_monitor_queue() {
static dispatch_queue_t lxd_fluecy_monitor_queue;
static dispatch_once_t once;
dispatch_once(&once, ^{
lxd_fluecy_monitor_queue = dispatch_queue_create("com.sindrilin.lxd_monitor_queue", NULL);
});
return lxd_fluecy_monitor_queue;
}
#define LOG_RUNLOOP_ACTIVITY 0
static void lxdRunLoopObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void * info) {
SHAREDMONITOR.currentActivity = activity;
dispatch_semaphore_signal(SHAREDMONITOR.semphore);
#if LOG_RUNLOOP_ACTIVITY
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"runloop entry");
break;
case kCFRunLoopExit:
NSLog(@"runloop exit");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"runloop after waiting");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"runloop before timers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"runloop before sources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"runloop before waiting");
break;
default:
break;
}
#endif
};
@implementation LXDAppFluecyMonitor
#pragma mark - Singleton override
+ (instancetype)sharedMonitor {
static LXDAppFluecyMonitor * sharedMonitor;
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedMonitor = [[super allocWithZone: NSDefaultMallocZone()] init];
[sharedMonitor commonInit];
});
return sharedMonitor;
}
+ (instancetype)allocWithZone: (struct _NSZone *)zone {
return [self sharedMonitor];
}
- (void)dealloc {
[self stopMonitoring];
}
- (void)commonInit {
self.semphore = dispatch_semaphore_create(0);
self.eventSemphore = dispatch_semaphore_create(0);
}
#pragma mark - Public
- (void)startMonitoring {
if (_isMonitoring) { return; }
_isMonitoring = YES;
CFRunLoopObserverContext context = {
0,
(__bridge void *)self,
NULL,
NULL
};
_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &lxdRunLoopObserverCallback, &context);
CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
dispatch_async(lxd_event_monitor_queue(), ^{
while (SHAREDMONITOR.isMonitoring) {
if (SHAREDMONITOR.currentActivity == kCFRunLoopBeforeWaiting) {
__block BOOL timeOut = YES;
NSLog(@"0");
dispatch_async(dispatch_get_main_queue(), ^{
timeOut = NO;
dispatch_semaphore_signal(SHAREDMONITOR.eventSemphore);
NSLog(@"1");
});
NSLog(@"2");
[NSThread sleepForTimeInterval: lxd_time_out_interval];
NSLog(@"3");
if (timeOut) {
NSLog(@"4");
[LXDBacktraceLogger lxd_logMain];
}
NSLog(@"5");
dispatch_wait(SHAREDMONITOR.eventSemphore, DISPATCH_TIME_FOREVER);
NSLog(@"6");
}
}
});
dispatch_async(lxd_fluecy_monitor_queue(), ^{
//while一直在循环换速度很快
//上面observer的回调函数会在runloop状态切换的时候发信号量
//信号量的发布频率 如果小于lxd_wait_interval 说明在这个状态超时了
//超时dispatch_semaphore_wait的返回值不为0
//下面过滤了当前runloop状态是kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting两种状态超时超时了五次lxd_wait_interval(说明代码有卡顿)
while (SHAREDMONITOR.isMonitoring) {
long waitTime = dispatch_semaphore_wait(self.semphore, dispatch_time(DISPATCH_TIME_NOW, lxd_wait_interval)); //返回值不为0的时候 表示信号量超时
if (waitTime != LXD_SEMPHORE_SUCCESS) {
if (!SHAREDMONITOR.observer) {
SHAREDMONITOR.timeOut = 0;
[SHAREDMONITOR stopMonitoring];
continue;
}
if (SHAREDMONITOR.currentActivity == kCFRunLoopBeforeSources || SHAREDMONITOR.currentActivity == kCFRunLoopAfterWaiting) {
if (++SHAREDMONITOR.timeOut < 5) {
continue; //提前结束本次 while循环
}
[LXDBacktraceLogger lxd_logMain];
[NSThread sleepForTimeInterval: lxd_restore_interval];
}
}
SHAREDMONITOR.timeOut = 0;
}
});
}
- (void)stopMonitoring {
if (!_isMonitoring) { return; }
_isMonitoring = NO;
CFRunLoopRemoveObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
CFRelease(_observer);
_observer = nil;
}
@end
4性能优化
待更新
5点击事件也和runloop有关
ios进程通信 一个进程向Mach port发消息 另一个进程监听Mach port
我们知道runloop没有事情做的时候就会休眠,那我们一个点击事假是怎么唤醒runloop的呢? 中间都发生了什么事情了呢?
//runloop的睡眠是和个函数卡主了
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy)
iOS中有很多进程通信的方式Mach Ports,Distributed Notifications,Distributed Objects,XPC
等等
这个函数等待接收mach_port
消息
当我们触发了事件(触摸/锁屏/摇晃等)后
由IOKit.framework生成一个 IOHIDEvent事件
而IOKit是苹果的硬件驱动框架
由它进行底层接口的抽象封装与系统进行交互传递硬件感应的事件
它专门处理用户交互设备,由IOHIDServices和IOHIDDisplays两部分组成
其中IOHIDServices是专门处理用户交互的,它会将事件封装成IOHIDEvents对象,详细请看这里
然后这些事件又由SpringBoard接收,它只接收收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event
接着用mach port
转发给需要的App进程
随后苹果注册的那个Source1
就会触发回调,并调用 _UIApplicationHandleEventQueue()
进行应用内部的分发
_UIApplicationHandleEventQueue()把IOHIDEvent处理包装成UIEvent进行处理分发.我们平时的UIGesture/处理屏幕旋转/发送给 UIWindow/UIButton 点击、touchesBegin/Move/End/Cancel这些事件,都是在这个回调中完成.
接下来就进入到寻找合适的view这个过程了
UIApplication->UIWindow->......->合适的hitView
找到合适的view就改响应事件了
合适的hitView-(没有响应事件)->superView-(没有响应事件)->...(所有superView都没有响应事件)...->UIWindow-(NO)-> UIApplication(默认没有响应)->丢弃这个UIEvent
点击事件总结借鉴(chaoxi) 哟_Json
触摸事件过程图片来自 JM_Seven