runloop前言
同runtime运行时,runloop为一个对象,为一个do...while运行循环,其用来保持程序的持续运行,处理程序中的各种事件,包括触摸、定时器、消息等,因此其作用不言而喻 runloop源码
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
runloop中一共有五个Ref结构:
CFRunLoopRef、CFRunLoopModeRef、CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObserverRef
runloopRef结构如下所示,其中有所在线程,持有的多个modes等
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef; //CFRunLoopRef
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopSource * CFRunLoopSourceRef;//CFRunLoopSourceRef
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopObserver * CFRunLoopObserverRef; //CFRunLoopObserverRef
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef; //CFRunLoopTimerRef
typedef struct __CFRunLoopMode *CFRunLoopModeRef; //CFRunLoopModeRef
//CFRunLoopRef的结构体数组
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与线程的关系
runloop依赖于线程而创建的,也就意味着线程存活,runloop才会存活,每一个线程都有唯一一个runloop与其对应,一般创建线程就创建好了,唯一区别主线程的runloop默认开启,子线程的runloop默认不开启
//通过main runloop的获取可以看到其实根据线程创建的
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
runloop与其说是保持程序持续运行,不如说保持该线程的持续运行更为贴切,所以子线程如果不主动开启runloop则执行完毕当前任务,该线程则会结束运行
//下面创建一个子线程,运行ruloop,与关闭线程的演示
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[NSRunLoop currentRunLoop] run];
if (isStop) {
[NSThread exit];
}
});
下图引用官方给的runloop的工作情况,其展现了Runloop在线程中的作用:从input source和timer source接受事件,然后在线程中处理事件
runloop的item
runloop主要处理下面6种类型的事件,也被成为runloop的item,即
// main dispatch queue GCD主队列
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
//Observer事件,runloop中状态变化时进行通知。
// Block事件,非延迟的NSObject PerformSelector立即调用,dispatch_after立即调用,block回调
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
// 响应source0,处理如UIEvent,CFSocket这类事件。需要手动触发。触摸事件其实是Source1接收系统事件后在回调 __IOHIDEventSystemClientQueueCallback() 内触发的 Source0,Source0 再触发的 _UIApplicationHandleEventQueue()。source0一定是要唤醒runloop及时响应并执行的,如果runloop此时在休眠等待系统的 mach_msg事件,那么就会通过source1来唤醒runloop执行
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
// source1,处理系统内核的mach_msg事件
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
// Timer事件,延迟的NSObject PerformSelector,延迟的dispatch_after,timer事件
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
例如主队列、timer、block的调用:
//主队列
dispatch_async(dispatch_get_main_queue(), ^{
});
//timer
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {}];
//block
void (^block)(void) = ^ {
};
block();
其中block的item结构如下所示,当需要执行的时候,会调用其block
struct _block_item {
struct _block_item *_next;
CFTypeRef _mode; // CFString or CFSet
void (^_block)(void);
}; //blockItem
runLoopMode
前面知道了runloop与线程为一对一的关系,并且runloop主要响应6中事件,即为其item,此外,每个runloop对象对应多个runLoopMode,每个runLoopMode表示着一种运行模式,如下所示:
kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
kCFRunLoopCommonModes: 这是一个占位的 Mode,当设置这个mode时,表示上面的所有mode模式下,事件都能正常响应。
runloop其运行模式会随着使用场景而切换,默认为kCFRunLoopDefaultMode下运行,当滚动视图滑动时mode会切换到UITrackingRunLoopMode中去
此外,每个runLoopMode都对应着多个item,例如:source(source0、source1)、observers、timers等,为一对多的关系
mode的结构体定义为CFRunLoopModeRef,源码如下所示:
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
//下面为runloop的items
CFMutableSetRef _sources0; //source源集合set
CFMutableSetRef _sources1;
CFMutableArrayRef _observers; //observer源集合Array
CFMutableArrayRef _timers; //timer源集合Array
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
因此,runloop、线程、mode、source、timer、observer其关系如下图所示
通过__CFRunLoopDoBlocks源码分析runloopMode的运行逻辑
经过CFRunLoopRun后,会走向CFRunLoopRunSpecific方法,在走到__CFRunLoopRun,这里面讲了runloop实际运行逻辑,各种item事件的执行,我们这里通过block的事件代码,来分析runloopMode的item执行过程
block的执行代码__CFRunLoopDoBlocks,如下所示
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
if (!rl->_blocks_head) return false;
if (!rlm || !rlm->_name) return false;
Boolean did = false;
struct _block_item *head = rl->_blocks_head;
struct _block_item *tail = rl->_blocks_tail;
rl->_blocks_head = NULL;
rl->_blocks_tail = NULL;
//获取所有的mode
CFSetRef commonModes = rl->_commonModes;
CFStringRef curMode = rlm->_name;
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
//获取item的的指针,保存上一个和头指针,可以看出其集合为链表数组
struct _block_item *prev = NULL;
struct _block_item *item = head;
//遍历item,毕竟一个runloop中有多个mode
while (item) {
struct _block_item *curr = item; //保存当前item,下一轮是为上一个item
//item指向下一个元素,方便遍历
item = item->_next;
Boolean doit = false;
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
// timer 加入的mode 和 我们现在runloop的mode 相等
// curr->_mode = kCFRunLoopCommonModes 相等
// mode中存在当前时间
达到以上条件即可执行doit为真
doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
} else {
//是否是包含关系的判断,实际逻辑与上面的一致
doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
}
if (!doit) prev = curr;
//满足条件
if (doit) {
if (prev) prev->_next = item;
if (curr == head) head = item;
if (curr == tail) tail = prev;
void (^block)(void) = curr->_block; //获取item的block
//清空modes和items,循环
CFRelease(curr->_mode);
free(curr);
//执行当前block
if (doit) {
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
did = true;
}
//释放当前block
Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
if (head) {
tail->_next = rl->_blocks_head;
rl->_blocks_head = head;
if (!rl->_blocks_tail) rl->_blocks_tail = tail;
}
return did;
}
以timer为例我们都会添加一个时间到当前runloop中,其中可以选择为defaultMode还是commonMode,都会调用__CFRunLoopAddItemToCommonModes方法来添加新的mode和item
static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
CFStringRef modeName = (CFStringRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
__CFRunLoopLock(rl);
if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
//加入item
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
/* add new item to all common-modes */
// timer -- items()
//遍历set递归添加
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
...
}
__CFRunLoopUnlock(rl);
}
CFRunLoopTimerRef
CFRunLoopTimerRef对象,CFRunLoopTimerRef其结构如下所示
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop; //当前的runloop
CFMutableSetRef _rlModes; //mode集合
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable */ 时间间隔
CFTimeInterval _tolerance; /* mutable */
uint64_t _fireTSR; /* TSR units */
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; /* immutable */ 回调
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
NSTimer对象的底层就是CFRunLoopTimerRef实现的,其模式也是会受到mode的切换所倾影响的,例如,使用下面这段代码
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"123");
}];
其生成的源码如下所示
(void)createCFTimer {
CFRunLoopTimerContext context = {
0,
((__bridge void *)self),
NULL,
NULL,
NULL
};
CFRunLoopRef rlp = CFRunLoopGetCurrent();//获取当前线程的runloop对象
/**
创建CFRunLoopTimerRef对象
参数一:用于分配对象的内存
参数二:在什么是触发 (距离现在)
参数三:每隔多少时间触发一次
参数四:未来参数
参数五:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
参数六:回调,比如触发事件,我就会来到这里
参数七:上下文记录信息
*/
CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 1, 0, 0, runLoopTimerCallBack, &context);
//添加到当前runloop当中,默认为kCFRunLoopDefaultMode
CFRunLoopAddTimer(rlp, timerRef, kCFRunLoopDefaultMode);
}
//回调方法
void runLoopTimerCallBack(CFRunLoopTimerRef timer, void *info){
}
CFRunLoopObserverRef
其对象如下所示
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities; /* immutable */
CFIndex _order; /* immutable */
CFRunLoopObserverCallBack _callout; /* immutable */ 回调
CFRunLoopObserverContext _context; /* immutable, except invalidation */会话
};
其活动状态activityes(CFRunLoopActivity)如下所示,其标识着runloop运行到某个阶段的状态,可以参考runloop的运行过程结合理解
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry , // 进入 loop
kCFRunLoopBeforeTimers , // 即将触发 Timer 回调
kCFRunLoopBeforeSources , // 即将触发 Source0 回调
kCFRunLoopBeforeWaiting , // 等待 mach_port 消息,进入休眠
kCFRunLoopAfterWaiting ), // 唤醒,接收 mach_port 消息
kCFRunLoopExit , // 退出 loop
kCFRunLoopAllActivities // loop 所有状态改变
}
observer为一个观察者,观察到runloop的状态改变,并回调指定函数,实现如下所示
- (void)createCFObsever {
//创建Observer会话层
CFRunLoopObserverContext context = {
0,
((__bridge void *)self),
NULL,
NULL,
NULL
};
//获取当前runloop
CFRunLoopRef rlp = CFRunLoopGetCurrent();
/**
参数一:用于分配对象的内存
参数二:你关注的事件 CFRunLoopActivity类型
参数三:CFRunLoopObserver是否循环调用
参数四:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
参数五:回调,比如触发事件,我就会来到这里
参数六:上下文记录信息
*/
CFRunLoopObserverRef observerRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, runLoopObserverCallBack, &context);
//加入到当前runloop中,模式为kCFRunLoopDefaultMode
CFRunLoopAddObserver(rlp, observerRef, kCFRunLoopDefaultMode);
}
//回调
void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
}
CFRunLoopSourceRef
CFRunLoopSourceRef为源,分为source0和source1,其为一个联合体结构(共享一片内存),表示source0和source1他们结构相同,根据其应用分为中,结构如下所示:
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits;
pthread_mutex_t _lock;
CFIndex _order; /* immutable */
CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0; /* immutable, except invalidation */
CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
} _context;
};
source0
处理App内部事件、APP自己负责管理(触发) 例如:UIEvent CFSocket
- (void)source0 {
//创建source会话层,最后三个参数分别为,待处理、取消、处理事件
CFRunLoopSourceContext context = {
0,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
schedule,
cancel,
perform,
};
/**
参数一:传递NULL或kCFAllocatorDefault以使用当前默认分配器。
参数二:优先级索引,指示处理运行循环源的顺序。这里我传0为了的就是自主回调
参数三:为运行循环源保存上下文信息的结构
*/
CFRunLoopSourceRef source0 = CFRunLoopSourceCreate(CFAllocatorGetDefault(), 0, &context);
CFRunLoopRef rlp = CFRunLoopGetCurrent();
// source runloop指定了mode,此时我们source就进入待绪状态
CFRunLoopAddSource(rlp, source0, kCFRunLoopDefaultMode);
// 一个执行信号,标记为待处理
CFRunLoopSourceSignal(source0);
// 唤醒run loop防止沉睡状态,处理事件
CFRunLoopWakeUp(rlp);
// 取消 移除
CFRunLoopRemoveSource(rlp, source0, kCFRunLoopDefaultMode);
CFRelease(rlp);
}
oid schedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode){
NSLog(@"准备工作");
}
void perform(void *info){
NSLog(@"处理事件");
}
void cancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode){
NSLog(@"取消了");
}
source1
包含一个 mach_port和一个回调(函数指针)
被用于通过内核和其他线程相互发送消息 NSPort通信这里就不介绍了
runloop运行过程
上面介绍了runloop的各种ref对象,以及与线程、mode、source、timer、observer等之间的对应关系,部分item的的获取以及使用(__CFRunLoopDoBlocks方法里面的item事件遍历)等
下面分析一下runloop源码,看看干了些什么
前面已经说了,主要事件为__CFRunLoopRun函数内部实现,由于内部逻辑非常长,这里将主要代码逻辑拿出查看(部分删除)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
/// 通知 Observers: 即将处理timer事件
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
/// 通知 Observers: 即将处理Source事件
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
/// 处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
/// 处理sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
/// 处理sources0返回为YES
if (sourceHandledThisLoop) {
/// 处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
}
/// 判断有无端口消息(Source1)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
/// 处理消息
goto handle_msg;
}
/// 通知 Observers: 即将进入休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
/// 等待被唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
// user callouts now OK again
__CFRunLoopUnsetSleeping(rl);
/// 通知 Observers: 被唤醒,结束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:
if (被Timer唤醒) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
/// 处理Timers
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
} else if (被GCD唤醒) {
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
/// 处理gcd
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else if (被Source1唤醒) {
CFRUNLOOP_WAKEUP_FOR_SOURCE();
/// 被Source1唤醒,处理Source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
/// 处理block
__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;
}
} while (0 == retVal);
return retVal;
}
通过上面代码,可以的得出runloop的运行,经过了如下步骤
1.通知Observer:即将进入loop
2.通知Observer:将要处理timer事件
3.通知Observer:将要处理Source0
4.处理blocks
5.处理source0
6.如果有source1,跳转到第10步骤
7.通知Observer:即将进入休眠
8.休眠,等待被唤醒
9.通知Observer:线程刚被唤醒
10.处理唤醒时收到的消息,跳回到步骤2
11.通知Observer:即将退出runloop
监听runloop回调
可以通过下面的方法监听runloop的执行过程
// 添加一个监听者
- (void)addRunLoopObserver {
// 1. 创建监听者
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault,
kCFRunLoopAllActivities, YES, 0,
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"进入RunLoop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理Timer事件");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理Source事件");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"被唤醒");
break;
case kCFRunLoopExit:
NSLog(@"退出RunLoop");
break;
default:
break;
}
});
// 2. 添加监听者
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
}
runloop的应用
通过上面流程可以了解,runloop会随着使用场景切换mode
timer使用注意:
因此使用timer时需要注意,根据需要将timer放到相应的mode中去,如果想要随时运行,那么可以设置mode为commoneMode,如果不那么注重时间可以加入默认mode
图片加载优化:
平时异步加载图片一般不会卡顿,当在加载大图时,会渲染比较慢(此过程一定是在主线程、滑动也同理),当滚动视图快速滑动大图时则会卡顿,因此可以利用runloop的defalutMode模式中渲染图片,避免快速滑动卡顿问题(注意此过程是设置image的过程不是下载的过程)
监控主线程卡顿:
根据runloop的运行流程,在主线程添加Observer监测Runloop各种状态变化,正常来说一般runloop不是在处理事件的路上就是睡眠,一旦发现处于睡眠前的kCFRunLoopBeforeSources状态或者唤醒后的状态kCFRunLoopAfterWaiting,且在设置的时间阈值内一直没有变化(即:卡在了唤醒之后处理source之前,步骤8->4之间,不是4->8),即可认定为卡顿。
自己可以尝试一下实现吧