1. 概述

正常情况下,程序执行完代码,就会自动退出

  1. int main(int argc, const char * argv[]) {
  2. @autoreleasepool {
  3. NSLog(@"执行任务");
  4. }
  5. return 0;
  6. }

App程序不能这样。App启动后要保持运行状态,当用户使用某些功能,程序要执行相应代码作出反馈。所以我们需要一个机制,让程序不退出并随时处理事件,例如:

  1. void run() {
  2. do {
  3. var message = get_next_message();
  4. process_message(message);
  5. } while (message != quit);
  6. }

这种模型通常被称作EventLoop。实现这种模型的关键点在于:如何管理事件/消息,如何让线程在没有处理消息时休眠以避免资源占用,在有消息到来时立刻被唤醒

EventLoop在很多系统和框架里都有实现,例如:OSX/iOS里的RunLoop

RunLoop是一个事件处理循环,属于线程相关基础架构的一部分。它用来安排工作,并协调接收传入的事件

RunLoop是一个do..while循环,和普通循环的区别,在有工作的时候让线程保持忙碌,没有工作时让线程休眠

官方文档:RunLoop

推荐视频:RunLoop的线下分享,by @sunnyxx

推荐文章:深入理解RunLoop

1.1 RunLoop作用

  • 保持程序的持续运行

  • 处理App中的各种事件(触摸、定时器、performSelector

  • 节省CPU资源,该做事就做事,该休息就休息

1.2 RunLoop API

OSX/iOS系统中,提供了NSRunLoopCFRunLoopRef两个对象:

  • NSRunLoop来自Foundation框架,是基于CFRunLoopRef的封装,提供了面向对象的API,但是这些API不是线程安全的

  • CFRunLoopRef来自CoreFoundation框架,它提供了C函数API,这些API是线程安全的

1.3 RunLoop源码

  • CFRunLoopRef的代码是开源的,可以下载 CoreFoundation 源码进行查看:

  • Swift开源后,苹果又维护了一个跨平台的 swift-corelibs-foundation 版本,和现有OC中的实现略不一样

2. RunLoop循环

Runloop在底层的封装为CFRunloop,一个do..while循环

  1. void CFRunLoopRun(void) { /* DOES CALLOUT */
  2. int32_t result;
  3. do {
  4. // 1.0e10 : 科学计数 1*10^10
  5. result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
  6. CHECK_FOR_FORK();
  7. } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
  8. }

从表面看,RunLoop就是一个死循环,但其内部CFRunLoopRunCFRunLoopRunSpecific__CFRunLoopRun__CFRunLoopServiceMachPort中,调用核心代码mach_msg函数,等待接受mach_port的消息。线程将进入休眠, 直到被下面某一个事件唤

所以,RunLoop并不是一个普通的死循环,它能保持程序的持续运行,但没事做的时候线程将进入休眠,从而节省CPU资源

3. 与线程的关系

苹果不允许直接创建RunLoop,它只提供了两个自动获取的函数:

  1. // 主运行循环
  2. CFRunLoopRef mainRunloop = CFRunLoopGetMain();
  3. // 当前运行循环
  4. CFRunLoopRef currentRunloop = CFRunLoopGetCurrent();

2.1 主运行循环

进入CFRunLoopGetMain函数

  1. CFRunLoopRef CFRunLoopGetMain(void) {
  2. CHECK_FOR_FORK();
  3. static CFRunLoopRef __main = NULL; // no retain needed
  4. if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
  5. return __main;
  6. }
  • 调用_CFRunLoopGet0函数,通过主线程查找

2.2 当前运行循环

进入CFRunLoopGetCurrent函数

  1. CFRunLoopRef CFRunLoopGetCurrent(void) {
  2. CHECK_FOR_FORK();
  3. //优先TSD中查找
  4. CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
  5. if (rl) return rl;
  6. //通过当前线程查找
  7. return _CFRunLoopGet0(pthread_self());
  8. }
  • 优先TSD中查找
  • 调用_CFRunLoopGet0函数,通过当前线程查找

2.3 _CFRunLoopGet0

进入_CFRunLoopGet0函数

  1. CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
  2. if (pthread_equal(t, kNilPthreadT)) {
  3. //不存在,默认为主线程
  4. t = pthread_main_thread_np();
  5. }
  6. __CFSpinLock(&loopsLock);
  7. if (!__CFRunLoops) {
  8. __CFSpinUnlock(&loopsLock);
  9. //创建全局字典,标记为kCFAllocatorSystemDefault
  10. CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
  11. //通过主线程 创建主运行循环
  12. CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
  13. // dict : key value
  14. //利用dict,进行key-value绑定操作,线程和Runloop是一一对应的
  15. CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
  16. if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
  17. CFRelease(dict);
  18. }
  19. CFRelease(mainLoop);
  20. __CFSpinLock(&loopsLock);
  21. }
  22. //通过其他线程获取Runloop
  23. CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
  24. __CFSpinUnlock(&loopsLock);
  25. if (!loop) {
  26. //如果没有获取到,创建一个Runloop
  27. CFRunLoopRef newLoop = __CFRunLoopCreate(t);
  28. __CFSpinLock(&loopsLock);
  29. loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
  30. if (!loop) {
  31. //与线程进行key-value绑定
  32. CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
  33. loop = newLoop;
  34. }
  35. // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
  36. __CFSpinUnlock(&loopsLock);
  37. CFRelease(newLoop);
  38. }
  39. if (pthread_equal(t, pthread_self())) {
  40. _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
  41. if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
  42. _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
  43. }
  44. }
  45. return loop;
  46. }
  • Runloop与线程一一对应

  • RunLoop对象在第一次获取RunLoop时创建,销毁则是在线程结束的时候

  • 主线程的RunLoop对象由系统自动创建,而子线程的RunLoop对象需要开发者主动创建

  • RunLoop并不保证线程安全

  • 当前线程内部不能操作其他线程的RunLoop对象

image.png

4. RunLoop结构

CoreFoundation里,包含RunLoop的五个类:

  • CFRunLoopRef

  • CFRunLoopModeRef

  • CFRunLoopSourceRef

  • CFRunLoopTimerRef

  • CFRunLoopObserverRef

其中CFRunLoopModeRef类并没有对外暴露,只是通过CFRunLoopRef的接口进行了封装。它们的关系如下:
image.png

一个RunLoop包含多个Mode,每个Mode又包含多个Source/Timer/Observer
image.png

每次调用RunLoop的主函数时,只能指定其中一个Mode,这个Mode称之为CurrentMode

如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入。这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响

4.1 CFRunLoopRef

创建RunLoop,返回CFRunLoopRef类型的对象

  1. static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
  2. CFRunLoopRef loop = NULL;
  3. CFRunLoopModeRef rlm;
  4. uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
  5. loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopTypeID, size, NULL);
  6. if (NULL == loop) {
  7. return NULL;
  8. }
  9. (void)__CFRunLoopPushPerRunData(loop);
  10. __CFRunLoopLockInit(&loop->_lock);
  11. loop->_wakeUpPort = __CFPortAllocate();
  12. if (CFPORT_NULL == loop->_wakeUpPort) HALT;
  13. __CFRunLoopSetIgnoreWakeUps(loop);
  14. loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
  15. CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
  16. loop->_commonModeItems = NULL;
  17. loop->_currentMode = NULL;
  18. loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
  19. loop->_blocks_head = NULL;
  20. loop->_blocks_tail = NULL;
  21. loop->_counterpart = NULL;
  22. loop->_pthread = t;
  23. #if DEPLOYMENT_TARGET_WINDOWS
  24. loop->_winthread = GetCurrentThreadId();
  25. #else
  26. loop->_winthread = 0;
  27. #endif
  28. rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
  29. if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
  30. return loop;
  31. }

CFRunLoopRef也是一个对象,本质是__CFRunLoop结构体类型的指针

  1. typedef struct __CFRunLoop * CFRunLoopRef;

__CFRunLoop结构体的定义:

  1. struct __CFRunLoop {
  2. CFRuntimeBase _base;
  3. pthread_mutex_t _lock; /* locked for accessing mode list */
  4. __CFPort _wakeUpPort; // used for CFRunLoopWakeUp
  5. Boolean _unused;
  6. volatile _per_run_data *_perRunData; // reset for runs of the run loop
  7. pthread_t _pthread;
  8. uint32_t _winthread;
  9. CFMutableSetRef _commonModes;
  10. CFMutableSetRef _commonModeItems;
  11. CFRunLoopModeRef _currentMode;
  12. CFMutableSetRef _modes;
  13. struct _block_item *_blocks_head;
  14. struct _block_item *_blocks_tail;
  15. CFTypeRef _counterpart;
  16. };
  • _commonModes_commonModeItems都是集合类型,表示在Runloop中可能会对应多个

这里有个概念叫commonModes

一个Mode可以将自己标记为Common属性,通过ModeName添加到RunLoopcommonModes

每当RunLoop的内容发生变化时,RunLoop都会自动将commonModeItems里的Source/Observer/Timer同步到具有Common标记的所有Mode

4.2 CFRunLoopMode

获取Runloop下的所有Modes

  1. CFRunLoopRef lp = CFRunLoopGetCurrent();
  2. CFArrayRef modeArray= CFRunLoopCopyAllModes(lp);
  3. NSLog(@"modeArray == %@",modeArray);
  4. -------------------------
  5. //输出以下内容:
  6. modeArray == (
  7. UITrackingRunLoopMode,
  8. GSEventReceiveRunLoopMode,
  9. kCFRunLoopDefaultMode
  10. )
  • 一个RunLoop对应多个Modes

创建一个NSTimer,将其加入Runloop并运行

  1. NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
  2. NSLog(@"fire in home -- %@",[[NSRunLoop currentRunLoop] currentMode]);
  3. }];
  4. [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
  • 添加到RunLoop中,必须指定Mode,这证明Timer的运行依赖于RunLoopMode

获取当前Runloop正在运行的Mode

  1. CFRunLoopRef lp = CFRunLoopGetCurrent();
  2. CFRunLoopMode mode = CFRunLoopCopyCurrentMode(lp);
  3. NSLog(@"mode == %@",mode);
  4. -------------------------
  5. //输出以下内容:
  6. mode == kCFRunLoopDefaultMode

4.2.1 类型

  • NSDefaultRunLoopMode:默认的Mode,正常情况下都在该Mode

  • NSConnectionReplyMode:将此模式与NSConnection对象结合使用来监视回复

  • NSModalPanelRunLoopMode:使用这种模式来识别用于模态面板的事件

  • NSEventTrackingRunLoopMode:使用该Mode跟踪来自用户交互的事件,例如:UITableView上下滑动

  • NSRunLoopCommonModes:伪模式,该集合默认包括默认、模态和事件跟踪模式

Mode主要是用于指定RunLoop中事件优先级

官方文档中,共提及以上五种Mode类型。而iOS中,只公开暴露了NSDefaultRunLoopModeNSRunLoopCommonModes两种

NSRunLoopCommonModes是伪模式,本质是Mode的集合,包含NSDefaultRunLoopModeNSEventTrackingRunLoopMode

4.2.2 结构

__CFRunLoopMode的结构体定义:

  1. struct __CFRunLoopMode {
  2. CFRuntimeBase _base;
  3. pthread_mutex_t _lock; /* must have the run loop locked before locking this */
  4. CFStringRef _name;
  5. CFMutableSetRef _sources0;
  6. CFMutableSetRef _sources1;
  7. CFMutableArrayRef _observers;
  8. CFMutableArrayRef _timers;
  9. ...
  10. };

4.2.3 作用

主线程的RunLoop里有两个预置的ModekCFRunLoopDefaultModeUITrackingRunLoopModeDefaultModeApp平时所处的状态,TrackingRunLoopMode是追踪ScrollView滑动时的状态

当创建一个Timer并加到DefaultMode时,Timer会得到重复回调,但此时滑动一个TableView时,RunLoop会将mode切换为TrackingRunLoopMode,这时Timer就不会被回调,并且也不会影响到滑动操作

如果Timer需要在两个Mode中都能得到回调:

  • 【方式一】将这个Timer分别加入这两个Mode

  • 【方式二】将Timer加入到顶层的RunLoopkCFRunLoopCommonModes中,commonModeItems会被RunLoop自动更新到所有具有Common属性的Mode里去

4.2.4 API

CFRunLoop对外暴露的管理ModeAPI

  1. CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);
  2. CFRunLoopRunInMode(CFStringRef modeName, ...);

只能通过modeName来操作内部的mode。当传入一个新的modeName,但RunLoop内部没有对应mode时,RunLoop会自动帮你创建对应的CFRunLoopModeRef

对于一个RunLoop来说,其内部的mode只能增加不能删除

4.3 Items

Source/Timer/Observer被统称item,一个item可以被同时加入多个mode。但一个item被重复加入同一个mode时是不会有效果的。如果一个mode中一个item都没有,则RunLoop会直接退出,不进入循环

Mode暴露的管理itemAPI

  1. CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
  2. CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
  3. CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
  4. CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
  5. CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
  6. CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

4.3.1 CFRunLoopSourceRef

CFRunLoopSourceRef是事件产生的地方。Source分为Source0Source1两种:

  • Source0:表示非系统事件,即用户自定义的事件

    • 只包含了一个函数指针,它并不能主动触发事件

    • 使用时,需要先调用CFRunLoopSourceSignal(source),将Source标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop)来唤醒RunLoop,让其处理这个事件

  • Source1:表示系统事件,主要负责底层的通讯,具备唤醒能力

    • RunLoop和内核管理,Mach port驱动

    • 包含了一个mach_port和一个函数指针,被用于通过内核和其他线程相互发送消息

    • 这种Source能主动唤醒RunLoop的线程

__CFRunLoopSource的定义:

  1. struct __CFRunLoopSource {
  2. CFRuntimeBase _base;
  3. uint32_t _bits;
  4. pthread_mutex_t _lock;
  5. CFIndex _order; /* immutable */
  6. CFMutableBagRef _runLoops;
  7. union {
  8. CFRunLoopSourceContext version0; /* immutable, except invalidation */
  9. CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
  10. } _context;
  11. };
  • Source0Source1在联合体中,实际使用中为其中的一种

Source0的定义:

  1. typedef struct {
  2. CFIndex version;
  3. void * info;
  4. const void *(*retain)(const void *info);
  5. void (*release)(const void *info);
  6. CFStringRef (*copyDescription)(const void *info);
  7. Boolean (*equal)(const void *info1, const void *info2);
  8. CFHashCode (*hash)(const void *info);
  9. void (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);
  10. void (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);
  11. void (*perform)(void *info);
  12. } CFRunLoopSourceContext;

Source1的定义:

  1. typedef struct {
  2. CFIndex version;
  3. void * info;
  4. const void *(*retain)(const void *info);
  5. void (*release)(const void *info);
  6. CFStringRef (*copyDescription)(const void *info);
  7. Boolean (*equal)(const void *info1, const void *info2);
  8. CFHashCode (*hash)(const void *info);
  9. #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
  10. mach_port_t (*getPort)(void *info);
  11. void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
  12. #else
  13. void * (*getPort)(void *info);
  14. void (*perform)(void *info);
  15. #endif
  16. } CFRunLoopSourceContext1;

4.3.2 CFRunLoopTimerRef

CFRunLoopTimerRef是基于时间的触发器,包含一个时间长度和一个函数指针。当其加入到RunLoop时,RunLoop会注册对应的时间点。当时间点到时,RunLoop会被唤醒以执行那个回调

__CFRunLoopTimer的定义:

  1. struct __CFRunLoopTimer {
  2. CFRuntimeBase _base;
  3. uint16_t _bits;
  4. pthread_mutex_t _lock;
  5. CFRunLoopRef _runLoop;
  6. CFMutableSetRef _rlModes;
  7. CFAbsoluteTime _nextFireDate;
  8. CFTimeInterval _interval; /* immutable */
  9. CFTimeInterval _tolerance; /* mutable */
  10. uint64_t _fireTSR; /* TSR units */
  11. CFIndex _order; /* immutable */
  12. CFRunLoopTimerCallBack _callout; /* immutable */
  13. CFRunLoopTimerContext _context; /* immutable, except invalidation */
  14. };

4.3.3 CFRunLoopObserverRef

CFRunLoopObserverRef是观察者,每个Observer都包含了一个函数指针,当RunLoop的状态发生变化时,观察者就能通过回调接受到这个变化

__CFRunLoopObserver的定义:

  1. struct __CFRunLoopObserver {
  2. CFRuntimeBase _base;
  3. pthread_mutex_t _lock;
  4. CFRunLoopRef _runLoop;
  5. CFIndex _rlCount;
  6. CFOptionFlags _activities; /* immutable */
  7. CFIndex _order; /* immutable */
  8. CFRunLoopObserverCallBack _callout; /* immutable */
  9. CFRunLoopObserverContext _context; /* immutable, except invalidation */
  10. };

可监听以下几种RunLoop的状态变化:

  1. /* Run Loop Observer Activities */
  2. typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
  3. //进入
  4. kCFRunLoopEntry = (1UL << 0),
  5. //即将处理Timers
  6. kCFRunLoopBeforeTimers = (1UL << 1),
  7. //即将处理Sources
  8. kCFRunLoopBeforeSources = (1UL << 2),
  9. //进入休眠
  10. kCFRunLoopBeforeWaiting = (1UL << 5),
  11. //被唤醒
  12. kCFRunLoopAfterWaiting = (1UL << 6),
  13. //退出
  14. kCFRunLoopExit = (1UL << 7),
  15. kCFRunLoopAllActivities = 0x0FFFFFFFU
  16. };

5. 事务处理

Runloop可处理以下事务类型:

  • Block应用:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

  • 调用timer__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__

  • 响应source0__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__

  • 响应source1__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__

  • GCD主队列:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

  • observer源:__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__

5.1 Timer的示例

创建CFRunLoopTimer,将其加入到Runloop并执行

  1. - (void)cfTimerDemo{
  2. CFRunLoopTimerContext context = {
  3. 0,
  4. ((__bridge void *)self),
  5. NULL,
  6. NULL,
  7. NULL
  8. };
  9. CFRunLoopRef rlp = CFRunLoopGetCurrent();
  10. /**
  11. 参数一:用于分配对象的内存
  12. 参数二:在什么是触发 (距离现在)
  13. 参数三:每隔多少时间触发一次
  14. 参数四:未来参数
  15. 参数五:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
  16. 参数六:回调,比如触发事件,我就会来到这里
  17. 参数七:上下文记录信息
  18. */
  19. CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 1, 0, 0, lgRunLoopTimerCallBack, &context);
  20. CFRunLoopAddTimer(rlp, timerRef, kCFRunLoopDefaultMode);
  21. }
  22. void lgRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info){
  23. NSLog(@"%@---%@",timer,info);
  24. }

进入CFRunLoopAddTimer函数

  1. void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
  2. CHECK_FOR_FORK();
  3. if (__CFRunLoopIsDeallocating(rl)) return;
  4. if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
  5. __CFRunLoopLock(rl);
  6. //匹配kCFRunLoopCommonModes,伪模式,集合类型
  7. if (modeName == kCFRunLoopCommonModes) {
  8. //获取集合
  9. CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
  10. //获取RunLoop下的事务
  11. if (NULL == rl->_commonModeItems) {
  12. //为空需要重新创建
  13. rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
  14. }
  15. //将传入的CFRunLoopTimer参数,加入到Items
  16. CFSetAddValue(rl->_commonModeItems, rlt);
  17. //集合不为空
  18. if (NULL != set) {
  19. CFTypeRef context[2] = {rl, rlt};
  20. /* add new item to all common-modes */
  21. //设置回调函数,添加到common-modes中
  22. CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
  23. CFRelease(set);
  24. }
  25. } else {
  26. //如果是其他类型,通过名字寻址Mode
  27. CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
  28. if (NULL != rlm) {
  29. if (NULL == rlm->_timers) {
  30. CFArrayCallBacks cb = kCFTypeArrayCallBacks;
  31. cb.equal = NULL;
  32. rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
  33. }
  34. }
  35. if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
  36. __CFRunLoopTimerLock(rlt);
  37. if (NULL == rlt->_runLoop) {
  38. rlt->_runLoop = rl;
  39. } else if (rl != rlt->_runLoop) {
  40. __CFRunLoopTimerUnlock(rlt);
  41. __CFRunLoopModeUnlock(rlm);
  42. __CFRunLoopUnlock(rl);
  43. return;
  44. }
  45. //如果匹配,将Runloop加进去,执行依赖于runloop run
  46. CFSetAddValue(rlt->_rlModes, rlm->_name);
  47. __CFRunLoopTimerUnlock(rlt);
  48. __CFRunLoopTimerFireTSRLock();
  49. __CFRepositionTimerInMode(rlm, rlt, false);
  50. __CFRunLoopTimerFireTSRUnlock();
  51. if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
  52. // Normally we don't do this on behalf of clients, but for
  53. // backwards compatibility due to the change in timer handling...
  54. if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
  55. }
  56. }
  57. if (NULL != rlm) {
  58. __CFRunLoopModeUnlock(rlm);
  59. }
  60. }
  61. __CFRunLoopUnlock(rl);
  62. }

CFRunLoopAddTimer源码中,没有找到执行事务的函数
image.png

  • 所以,真正事务的执行,依赖于runloop run

5.2 __CFRunLoopRun

进入CFRunLoopRun函数

  1. void CFRunLoopRun(void) { /* DOES CALLOUT */
  2. int32_t result;
  3. do {
  4. result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
  5. CHECK_FOR_FORK();
  6. } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
  7. }
  • 通过CFRunLoopRunSpecific函数,得到result结果
  • 1.0e10:科学计数法,1 * 10 ^ 10

进入CFRunLoopRunSpecific函数

  1. SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
  2. CHECK_FOR_FORK();
  3. if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
  4. __CFRunLoopLock(rl);
  5. //根据modeName找到本次运行的mode
  6. CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
  7. //如果没找到 || mode中没有注册任何事件,则就此停止,不进入循环
  8. if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
  9. Boolean did = false;
  10. if (currentMode) __CFRunLoopModeUnlock(currentMode);
  11. __CFRunLoopUnlock(rl);
  12. return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
  13. }
  14. volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
  15. //取上一次运行的mode
  16. CFRunLoopModeRef previousMode = rl->_currentMode;
  17. //如果本次mode和上次的mode一致
  18. rl->_currentMode = currentMode;
  19. //初始化一个result为kCFRunLoopRunFinished
  20. int32_t result = kCFRunLoopRunFinished;
  21. if (currentMode->_observerMask & kCFRunLoopEntry )
  22. /// 1. 通知 Observers: RunLoop 即将进入 loop。
  23. __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
  24. /// 真正做事情的代码,调用__CFRunLoopRun函数
  25. result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
  26. if (currentMode->_observerMask & kCFRunLoopExit )
  27. /// 10. 通知 Observers: RunLoop 即将退出。
  28. __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
  29. __CFRunLoopModeUnlock(currentMode);
  30. __CFRunLoopPopPerRunData(rl, previousPerRun);
  31. rl->_currentMode = previousMode;
  32. __CFRunLoopUnlock(rl);
  33. return result;
  34. }

进入__CFRunLoopRun函数
image.png

  • 每一种类型都有特定的处理函数调用

5.3 __CFRunLoopDoTimer

进入__CFRunLoopDoTimers函数

  1. static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) { /* DOES CALLOUT */
  2. Boolean timerHandled = false;
  3. CFMutableArrayRef timers = NULL;
  4. //程序中正在运行的timer可能不止一个
  5. for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++) {
  6. //遍历timer,从Mode中获取
  7. CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);
  8. if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) {
  9. if (rlt->_fireTSR <= limitTSR) {
  10. if (!timers) timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
  11. CFArrayAppendValue(timers, rlt);
  12. }
  13. }
  14. }
  15. for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) {
  16. CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
  17. //执行timer
  18. Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
  19. timerHandled = timerHandled || did;
  20. }
  21. if (timers) CFRelease(timers);
  22. return timerHandled;
  23. }

进入__CFRunLoopDoTimer函数
image.png

  • 进行timer的回调

5.4 处理流程

  • 通过CFRunLoopAddTimertimer加入到指定Mode

  • 事务的执行依赖于runloop run

    • 调用CFRunLoopRunCFRunLoopRunSpecific函数,在Runloop进入和离开之间,调用__CFRunLoopRun函数
  • __CFRunLoopRun中,包含对ObserversSource0Source1Timer的逻辑处理

    • 针对timer的处理,调用__CFRunLoopDoTimers,遍历当前正在运行的timer

    • 针对单个timer,调用__CFRunLoopDoTimer函数,执行事务的处理

RunLoop事务处理的流程图:
image.png

6. 底层原理

6.1 CFRunLoopRunSpecific伪代码

  1. SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
  2. CHECK_FOR_FORK();
  3. if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
  4. __CFRunLoopLock(rl);
  5. /// 首先根据modeName找到对应mode
  6. CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
  7. /// 1.通知 Observers: RunLoop 即将进入 loop。
  8. __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
  9. /// 内部函数,进入loop
  10. result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
  11. /// 10.通知 Observers: RunLoop 即将退出。
  12. __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
  13. return result;
  14. }

6.2 __CFRunLoopRun伪代码

  1. static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
  2. int32_t retVal = 0;
  3. do { // itmes do
  4. /// 2.通知 Observers: 即将处理timer事件
  5. __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
  6. /// 3.通知 Observers: 即将处理Source事件
  7. __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
  8. /// 处理Blocks
  9. __CFRunLoopDoBlocks(rl, rlm);
  10. /// 4.处理sources0
  11. Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
  12. /// 处理sources0返回为YES
  13. if (sourceHandledThisLoop) {
  14. /// 处理Blocks
  15. __CFRunLoopDoBlocks(rl, rlm);
  16. }
  17. /// 5.判断有无端口消息(Source1)
  18. if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
  19. /// 如果有Source1,跳到第9步,处理消息
  20. goto handle_msg;
  21. }
  22. /// 6.通知 Observers: 即将进入休眠
  23. __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
  24. __CFRunLoopSetSleeping(rl);
  25. /// 7.等待被唤醒
  26. __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
  27. // user callouts now OK again
  28. __CFRunLoopUnsetSleeping(rl);
  29. /// 8.通知 Observers: 被唤醒,结束休眠
  30. __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
  31. /// 9.处理唤醒时收到的消息
  32. handle_msg:
  33. if (被Timer唤醒) {
  34. /// 处理Timers
  35. __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
  36. } else if (被GCD唤醒) {
  37. /// 处理gcd
  38. __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
  39. } else if (被Source1唤醒) {
  40. /// 被Source1唤醒,处理Source1
  41. __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
  42. }
  43. /// 处理block
  44. __CFRunLoopDoBlocks(rl, rlm);
  45. if (sourceHandledThisLoop && stopAfterHandle) {
  46. /// 进入loop时参数说处理完事件就返回
  47. retVal = kCFRunLoopRunHandledSource;
  48. } else if (timeout_context->termTSR < mach_absolute_time()) {
  49. /// 超出传入参数标记的超时时间了
  50. retVal = kCFRunLoopRunTimedOut;
  51. } else if (__CFRunLoopIsStopped(rl)) {
  52. /// 被外部调用者强制停止了
  53. __CFRunLoopUnsetStopped(rl);
  54. retVal = kCFRunLoopRunStopped;
  55. } else if (rlm->_stopped) {
  56. rlm->_stopped = false;
  57. retVal = kCFRunLoopRunStopped;
  58. } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
  59. /// source/timer/observer一个都没有了
  60. retVal = kCFRunLoopRunFinished;
  61. }
  62. /// 如果没超时,mode里没空,loop也没被停止,那继续loop。
  63. } while (0 == retVal);
  64. return retVal;
  65. }

6.3 流程图

image.png

7. RunLoop实战应用

7.1 线程保活

iOS开发中,有些耗时操作会阻塞主线程,导致界面卡顿,此时我们会创建一个子线程,而后把这些耗时的操作放在子线程中处理

一个常规线程,任务执行完后就会销毁。如果耗时操作经常需要在子线程中执行,频繁创建和销毁线程,会造成资源浪费。 这时我们要用一种方式,让该线程长时间存活而不被销毁

线程保活的三种方式:

  • NSTimer

  • 条件锁(NSCondition

  • RunLoopPort

7.1.1 NSTimer

  1. @interface ViewController ()
  2. @property (nonatomic, assign) BOOL isStopping;
  3. @property (nonatomic, strong) LGThread *thread;
  4. @end
  5. @implementation ViewController
  6. - (void)viewDidLoad {
  7. [super viewDidLoad];
  8. _thread = [[LGThread alloc] initWithBlock:^{
  9. [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
  10. NSLog(@"hello");
  11. if (self.isStopping) {
  12. // 先将对象置为nil
  13. self.thread = nil;
  14. // 再退出线程,RunLoop也停止了
  15. [NSThread exit];
  16. }
  17. }];
  18. [[NSRunLoop currentRunLoop] run];
  19. }];
  20. self.thread.name = @"LG";
  21. [self.thread start];
  22. }
  23. -(void)viewDidAppear:(BOOL)animated{
  24. [super viewDidAppear:animated];
  25. //为常驻线程添加耗时任务
  26. [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
  27. }
  28. -(void)test{
  29. NSLog(@"耗时任务开始");
  30. sleep(5);
  31. NSLog(@"耗时任务结束");
  32. }
  33. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
  34. self.isStopping = YES;
  35. }
  36. @end

7.1.2 条件锁(NSCondition

  1. @interface ViewController ()
  2. @property (nonatomic, assign) BOOL isStopping;
  3. @property (nonatomic, strong) LGThread *thread;
  4. @property (nonatomic, strong) NSCondition *condition;
  5. @property (nonatomic, strong) void(^blockTask)(void);
  6. @end
  7. @implementation ViewController
  8. - (void)viewDidLoad {
  9. [super viewDidLoad];
  10. _thread = [[LGThread alloc] initWithBlock:^{
  11. @autoreleasepool {
  12. do {
  13. NSLog(@"hello");
  14. [self.condition lock];
  15. if(self.blockTask){
  16. self.blockTask();
  17. self.blockTask = nil;
  18. }
  19. NSLog(@"闲等...");
  20. [self.condition wait];
  21. [self.condition unlock];
  22. } while (!self.isStopping);
  23. //将对象置为nil
  24. self.thread = nil;
  25. }
  26. }];
  27. self.thread.name = @"LG";
  28. [self.thread start];
  29. }
  30. -(void)viewDidAppear:(BOOL)animated{
  31. [super viewDidAppear:animated];
  32. //为常驻线程添加耗时任务
  33. self.blockTask = ^(void){
  34. NSLog(@"耗时任务开始");
  35. sleep(5);
  36. NSLog(@"耗时任务结束");
  37. };
  38. //通知来任务了,唤醒条件锁
  39. [self.condition signal];
  40. }
  41. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
  42. self.isStopping = YES;
  43. [self.condition signal];
  44. }
  45. - (NSCondition *)condition {
  46. if(!_condition){
  47. _condition = [[NSCondition alloc] init];
  48. }
  49. return _condition;
  50. }
  51. @end

7.1.3 RunLoopPort

  1. @interface ViewController ()
  2. @property (nonatomic, assign) BOOL isStopping;
  3. @property (nonatomic, strong) LGThread *thread;
  4. @end
  5. @implementation ViewController
  6. - (void)viewDidLoad {
  7. [super viewDidLoad];
  8. _thread = [[LGThread alloc] initWithBlock:^{
  9. NSRunLoop *loop = [NSRunLoop currentRunLoop];
  10. [loop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
  11. while (!self.isStopping) {
  12. [loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  13. }
  14. self.thread = nil;
  15. }];
  16. self.thread.name = @"LG";
  17. [self.thread start];
  18. }
  19. -(void)viewDidAppear:(BOOL)animated{
  20. [super viewDidAppear:animated];
  21. //为常驻线程添加耗时任务
  22. [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
  23. }
  24. -(void)test{
  25. NSLog(@"耗时任务开始");
  26. sleep(5);
  27. NSLog(@"耗时任务结束");
  28. }
  29. - (void)exitThread{
  30. self.isStopping = YES;
  31. }
  32. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
  33. [self performSelector:@selector(exitThread) onThread:self.thread withObject:nil waitUntilDone:NO];
  34. }
  35. @end

这种方式,需要注意以下几点:

  • 在子线程中创建的RunLoop,必须添加TimerSource0Source1中的其中一项。如果都不存在,RunLoop无法运行。但是主RunLoop则不同,即使都不存在也会持续运行

image.png

  • addPort本质上就是添加了一个Source1,它能主动唤醒RunLoop的线程。Source1的存储采用字典,以当前Source1 ProtkeyCFRunLoopSourceRefvalue。而Source0直接使用数组存储,它并不能主动触发事件

image.png

  • 需要使用runMode:方法运行RunLoop,不能使用run方法。后者是永久循环,无法退出

image.png

  • 使用runMode:方法,传入的ModeNSDefaultRunLoopMode,不能传入NSRunLoopCommonModes。后者会被标记为kCFRunLoopRunFinished,导致RunLoop直接退出

image.png

7.2 App回光返照

  1. + (void)installUncaughtExceptionHandler {
  2. NSSetUncaughtExceptionHandler(&LGExceptionHandlers);
  3. }
  4. - (void)lg_handleException:(NSException *)exception{
  5. //处理报错信息,可以写入沙盒文件,下次启动时上传服务器
  6. [self validateAndSaveCriticalApplicationData:exception];
  7. ...
  8. while (!self.dismissed) {
  9. for (NSString *mode in (__bridge NSArray *)allModes) {
  10. //快速切换Mode
  11. CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
  12. }
  13. }
  14. CFRelease(allModes);
  15. NSSetUncaughtExceptionHandler(NULL);
  16. }
  • 系统提供的NSSetUncaughtExceptionHandler函数,传入LGExceptionHandlers函数地址

  • 当出现Crash,会自动触发LGExceptionHandlers回调函数

  • 在回调函数中,拿到当前RunLoop,监听所有Mode

这种方式,相当于应用程序自启的Runloop的平行空间,跟着应用程序保活,并具备响应能力,也就是App的回光返照

7.3 卡顿检测

通过监听主RunLoop的事务变化进行卡顿检测

  1. - (void)start{
  2. [self registerObserver];
  3. [self startMonitor];
  4. }
  5. static void CallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
  6. {
  7. LGBlockMonitor *monitor = (__bridge LGBlockMonitor *)info;
  8. monitor->activity = activity;
  9. // 发送信号
  10. dispatch_semaphore_t semaphore = monitor->_semaphore;
  11. dispatch_semaphore_signal(semaphore);
  12. }
  13. - (void)registerObserver{
  14. CFRunLoopObserverContext context = {0, (__bridge void*)self, NULL, NULL};
  15. //NSIntegerMax : 优先级最小
  16. CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
  17. kCFRunLoopAllActivities,
  18. YES,
  19. NSIntegerMax,
  20. &CallBack,
  21. &context);
  22. CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
  23. }
  24. - (void)startMonitor{
  25. // 创建信号
  26. _semaphore = dispatch_semaphore_create(0);
  27. // 在子线程监控时长
  28. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  29. while (YES)
  30. {
  31. // 超时时间是 1 秒,没有等到信号量,st 就不等于 0, RunLoop 所有的任务
  32. long st = dispatch_semaphore_wait(self->_semaphore, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC));
  33. if (st != 0)
  34. {
  35. if (self->activity == kCFRunLoopBeforeSources || self->activity == kCFRunLoopAfterWaiting)
  36. {
  37. if (++self->_timeoutCount < 2){
  38. NSLog(@"timeoutCount==%lu",(unsigned long)self->_timeoutCount);
  39. continue;
  40. }
  41. // 一秒左右的衡量尺度 很大可能性连续来 避免大规模打印!
  42. NSLog(@"检测到超过两次连续卡顿");
  43. }
  44. }
  45. self->_timeoutCount = 0;
  46. }
  47. });
  48. }
  • 定义信号量,在全局并发队列中,加入异步函数,创建while死循环,内部让信号量进入休眠状态,定义一秒的超时时间

  • 监听主RunLoop的所有事务,在回调方法中,对信号量发送释放的通知

  • 如果信号量时间,检查Observer的状态。如果是处理Sources或处于唤醒状态,证明还在做事情,将超时次数+1

  • 如果连续两次,视为卡顿

解决方式:

可以考虑将部分业务代码,在Observer回调函数中,RunLoop处于kCFRunLoopBeforeWaiting状态时运行。因为RunLoop进入即将休眠状态时,此时没有其他任务,CPU等资源相对空闲。这种方式相当于代码的错峰执行,可以对卡顿有一定程度的优化

总结

RunLoop作用:

  • 保持程序的持续运行

  • 处理App中的各种事件(触摸、定时器、performSelector

  • 节省CPU资源,该做事就做事,该休息就休息


与线程的关系:

  • RunLoop与线程一一对应

  • RunLoop对象在第一次获取RunLoop时创建,销毁则是在线程结束的时候

  • 主线程的RunLoop对象由系统自动创建,而子线程的RunLoop对象需要开发者主动创建

  • RunLoop并不保证线程安全

  • 当前线程内部不能操作其他线程的RunLoop对象

结构:

  • 线程与RunLoop一对一

  • 一个RunLoop对应多个Modes

  • 一个Mode对应多个Items

  • SourceTimerObserver统称Item

Mode

  • Mode主要是用于指定RunLoop中事件优先级

  • NSDefaultRunLoopMode:默认的Mode,正常情况下都在该Mode

  • NSConnectionReplyMode:将此模式与NSConnection对象结合使用来监视回复

  • NSModalPanelRunLoopMode:使用这种模式来识别用于模态面板的事件

  • NSEventTrackingRunLoopMode:使用该Mode跟踪来自用户交互的事件,例如:UITableView上下滑动

  • NSRunLoopCommonModes:伪模式,该集合默认包括默认、事件跟踪模式

Runloop可处理以下事务(Item)类型:

  • Block应用:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

  • 调用timer__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__

  • 响应source0__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__

  • 响应source1__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__

  • GCD主队列:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

  • observer源:__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__

Source

  • 可以唤醒RunLoop的一些事件

  • Source0:表示非系统事件,即用户自定义的事件

  • Source1:表示系统事件,主要负责底层的通讯,具备唤醒能力

Timer

  • 常用NSTimer定时器

Observer

  • 用于监听RunLoop的状态变化,并作出一定响应

  • kCFRunLoopEntry:进入

  • kCFRunLoopBeforeTimers:即将处理Timers

  • kCFRunLoopBeforeSources:即将处理Sources

  • kCFRunLoopBeforeWaiting:进入休眠

  • kCFRunLoopAfterWaiting:被唤醒

  • kCFRunLoopExit:退出

事务处理流程:

  • timer为例,通过CFRunLoopAddTimertimer加入到指定Mode

  • 事务的执行依赖于runloop run

  • __CFRunLoopRun中,包含对ObserversSource0Source1Timer的逻辑处理

底层原理:

  • 【第一步】通知Observer即将进入LoopObserver

  • 【第二步】通知Observer将要处理TimerObserver

  • 【第三步】通知Observer将要处理Source0Observer

  • 【第四步】处理Source0Source0

  • 【第五步】如果有Source1,跳到【第九步】(Source1

  • 【第六步】通知Observer线程即将休眠(Observer

  • 【第七步】休眠,等待唤醒。可通过Source1(port)Timer、外部手动唤醒

  • 【第八步】通过Observer线程刚被唤醒(Observer

  • 【第九步】处理唤醒时收到的消息,之后跳回【第二步】(TimerSource1

  • 【第十步】通知Observer即将退出LoopObserver