RunLoop

RunLoop顾名思义运行循环,在程序运行过程中循环做一些事情,比如:定时器(NSTimer)、GCD Async Main Queue、事件响应、手势识别、界面刷新、网络请求、AutoreleasePool;在没有RunLoop时,程序会立刻退出(比如创建的命令行项目)

一、RunLoop与线程

每条线程都有唯一的一个与之对应的RunLoop对象;RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value;线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建;RunLoop会在线程结束时销毁;主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

  • iOS中有2套API来访问和使用RunLoop
    • Foundation:NSRunLoop
    • Core Foundation:CFRunLoopRef
    • NSRunLoop是基于CFRunLoopRef的一层OC包装
  • CFRunLoopRef源码
  • 常用获取RunLoop的API
    //获取当前线程RunLoop
    [NSRunLoop currentRunLoop];
    CFRunLoopGetCurrent();
    
    //获取主线程RunLoop
    [NSRunLoop mainRunLoop];
    CFRunLoopGetMain();

二、RunLoop

Core Foundation中与RunLoop相关的5个类

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopSource * CFRunLoopSourceRef;

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopObserver * CFRunLoopObserverRef;

typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;
  • __CFRunLoop结构
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; //集合,存放commonMode
    CFMutableSetRef _commonModeItems; //集合,存放选择commonMode中任务
    CFRunLoopModeRef _currentMode; //当前的mode类型
    CFMutableSetRef _modes; //集合,存储mode
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
}
  • __CFRunLoopMode结构
struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0; //集合,source0
    CFMutableSetRef _sources1;  //集合,source1
    CFMutableArrayRef _observers; //集合,observer
    CFMutableArrayRef _timers; //集合,timer
    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 */
}
  • __CFRunLoopSource结构
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;
}
  • __CFRunLoopObserver结构
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 */
}
  • __CFRunLoopTimer结构
struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;       /* immutable */
    CFTimeInterval _tolerance;          /* mutable */
    uint64_t _fireTSR;          /* TSR units */
    CFIndex _order;         /* immutable */
    CFRunLoopTimerCallBack _callout;    /* immutable */
    CFRunLoopTimerContext _context; /* immutable, except invalidation */
}

RunLoop中5个类之间的关系
RunLoop_第1张图片
RunLoop.png

  • 1、CFRunLoopRef:RunLoop对象
  • 2、CFRunLoopModeRef:RunLoop的运行模式
    • 一个RunLoop包含多个Mode,每个Mode中包含多个Source0/Source1/Timer/Observer
    • RunLoop只能选择其中一个Mode,作为currentMode
    • 如果需要切换Mode,只能退出当前Mode,再重新选择一个Mode进入
    • 不同Mode的Source0/Source1/Timer/Observer是分割开来,互不影响
    • 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立即退出
    • KCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
    • UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响
    • NSRunLoopCommonModes:其实这并不是一个真正的Mode模式,它只是一个标记;使用NSRunLoopCommonModes后,timer或者任务可以在_commonModes集合中存放的模式下工作。NSDefaultRunLoopMode或者UITrackingRunLoopMode都是有NSRunLoopCommonModes标记的
  • 3、CFRunLoopObserverRef
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),           //即将进入RunLoop
        kCFRunLoopBeforeTimers = (1UL << 1),    //即将处理Timer
        kCFRunLoopBeforeSources = (1UL << 2),   //即将处理Source
        kCFRunLoopBeforeWaiting = (1UL << 5),   //即将进入休眠
        kCFRunLoopAfterWaiting = (1UL << 6),    //从休眠中唤醒
        kCFRunLoopExit = (1UL << 7),            //退出RunLoop
        kCFRunLoopAllActivities = 0x0FFFFFFFU
    }

RunLoop状态 & RunLoop Mode切换实践

1、RunLoop状态

  • 测试代码
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self observerFun];
}


/**
 callback
 */
void runloopObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"kCFRunLoopEntry");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"kCFRunLoopBeforeTimers");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"kCFRunLoopBeforeSources");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"kCFRunLoopBeforeWaiting");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"kCFRunLoopAfterWaiting");
            break;
        case kCFRunLoopExit:
            NSLog(@"kCFRunLoopExit");
            break;
        default:
            break;
    }
}


/**
 observer 方法回调
 */
- (void)observerFun {
    //创建observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, runloopObserver, NULL);
    //添加observer到RunLoop
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
    //释放
    CFRelease(observer);
}
@end
  • 打印输出
2018-07-31 22:32:29.778493+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.780044+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.780857+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.783191+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.783808+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.784232+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.785235+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.785388+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.785558+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.786129+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.786779+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.786998+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.789340+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.790210+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.792530+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.793507+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.798424+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.798630+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.800387+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.802476+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.802757+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:32:29.805617+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:32:29.805905+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.806267+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.806377+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:32:29.844311+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:32:29.845274+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.845789+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.846403+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:32:29.925660+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:32:29.926847+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:29.928050+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:29.928671+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:32:30.041824+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:32:30.042216+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:30.042762+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:30.042918+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:32:30.178113+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:32:30.219570+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:30.219786+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:30.220533+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:32:30.274901+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:32:30.277101+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:30.277269+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:30.277508+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:32:30.767515+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:32:30.767774+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:30.767892+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:30.767997+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:32:31.457565+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:32:31.458090+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:32:31.458287+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:32:31.458479+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting
2018-07-31 22:33:00.001934+0800 01-RunLoop[5232:384864] kCFRunLoopAfterWaiting
2018-07-31 22:33:00.004481+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeTimers
2018-07-31 22:33:00.005199+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeSources
2018-07-31 22:33:00.006257+0800 01-RunLoop[5232:384864] kCFRunLoopBeforeWaiting

2、RunLoop Mode切换

  • 测试代码
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self observerFun_block];
}

/**
 observer block回调
 */
- (void)observerFun_block {
    // 创建Observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry: {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopEntry - %@", mode);
                CFRelease(mode);
                break;
            }
                
            case kCFRunLoopExit: {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopExit - %@", mode);
                CFRelease(mode);
                break;
            }
                
            default:
                break;
        }
    });
    // 添加Observer到RunLoop中
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    // 释放
    CFRelease(observer);
}

@end
  • 打印输出
2018-07-31 22:35:18.153490+0800 01-RunLoop[5262:386889] kCFRunLoopExit - kCFRunLoopDefaultMode
2018-07-31 22:35:18.153723+0800 01-RunLoop[5262:386889] kCFRunLoopEntry - UITrackingRunLoopMode
2018-07-31 22:35:25.399625+0800 01-RunLoop[5262:386889] kCFRunLoopExit - UITrackingRunLoopMode
2018-07-31 22:35:25.399830+0800 01-RunLoop[5262:386889] kCFRunLoopEntry - kCFRunLoopDefaultMode
  • 当滑动UIScrollView时,Mode退出kCFRunLoopDefaultMode,进入UITrackingRunLoopMode
  • 当UIScrollView滑动停止时,Mode退出UITrackingRunLoopMode,进入kCFRunLoopDefaultMode

三、RunLoop运行逻辑

RunLoop会被Port、custom、performSelector:onThread:、Timer唤醒
RunLoop会处理handlePort、customSrc、mySelector、timerFired这些任务

RunLoop_第2张图片
RunLoop运行逻辑.png

1、RunLoop循环过程

  • 1、通知Observers:进入Loop
  • 2、通知Observers:即将处理Timers
  • 3、通知Observers:即将处理Sources
  • 4、处理Blocks
  • 5、处理Source0(可能会再次处理Blocks)
  • 6、如果存在Source1,就跳转到第8步
  • 7、通知Observers:开始休眠(等待消息唤醒)
  • 8、通知Observers:结束休眠(被某个消息唤醒)
    • 处理Timer
    • 处理GCD Async To Main Queue
  • 9、处理Blocks
  • 10、根据前面的执行结果,决定如何操作
    • 回到第2步
    • 退出Loop
  • 11、通知Observers:退出Loop
RunLoop_第3张图片
RunLoop运行过程.png

2、源码分析

RunLoop_第4张图片
RunLoop源码.png

1、RunLoop入口

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000108f8d57f 01-RunLoop`__35-[ViewController observerFun_block]_block_invoke(.block_descriptor=0x0000000108f8f0d0, observer=0x0000604000321540, activity=kCFRunLoopEntry) at ViewController.m:74
    frame #1: 0x000000010992e607 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    frame #2: 0x000000010992e55e CoreFoundation`__CFRunLoopDoObservers + 430
    frame #3: 0x00000001099122e7 CoreFoundation`CFRunLoopRunSpecific + 599 //RunLoop入口
    frame #4: 0x000000010f404a73 GraphicsServices`GSEventRunModal + 62
    frame #5: 0x000000010a7890b7 UIKit`UIApplicationMain + 159
    frame #6: 0x0000000108f8d66f 01-RunLoop`main(argc=1, argv=0x00007ffee6c71fe8) at main.m:14
    frame #7: 0x000000010dcf3955 libdyld.dylib`start + 1
    frame #8: 0x000000010dcf3955 libdyld.dylib`start + 1

2、CFRunLoopRunSpecific

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
    Boolean did = false;
    if (currentMode) __CFRunLoopModeUnlock(currentMode);
    __CFRunLoopUnlock(rl);
    return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    CFRunLoopModeRef previousMode = rl->_currentMode;
    rl->_currentMode = currentMode;
    int32_t result = kCFRunLoopRunFinished;

#pragma mark - 通知Observer Entry
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
#pragma mark - 运行RunLoop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
#pragma mark - 通知Observers Exit
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
    rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}

3、__CFRunLoopRun运行过程

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    uint64_t startTSR = mach_absolute_time();

    if (__CFRunLoopIsStopped(rl)) {
        __CFRunLoopUnsetStopped(rl);
    return kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
    rlm->_stopped = false;
    return kCFRunLoopRunStopped;
    }
    
    mach_port_name_t dispatchPort = MACH_PORT_NULL;
    Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
    if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();
    
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    mach_port_name_t modeQueuePort = MACH_PORT_NULL;
    if (rlm->_queue) {
        modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
        if (!modeQueuePort) {
            CRASH("Unable to get port for run loop mode queue (%d)", -1);
        }
    }
#endif
    
    dispatch_source_t timeout_timer = NULL;
    struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
    if (seconds <= 0.0) { // instant timeout
        seconds = 0.0;
        timeout_context->termTSR = 0ULL;
    } else if (seconds <= TIMER_INTERVAL_LIMIT) {
    dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();
    timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_retain(timeout_timer);
    timeout_context->ds = timeout_timer;
    timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
    timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
    dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
    dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
        dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
        uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
        dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
        dispatch_resume(timeout_timer);
    } else { // infinite timeout
        seconds = 9999999999.0;
        timeout_context->termTSR = UINT64_MAX;
    }

    Boolean didDispatchPortLastTime = true;
    int32_t retVal = 0;
    do {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;
        voucher_t voucherCopy = NULL;
#endif
        uint8_t msg_buffer[3 * 1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        mach_msg_header_t *msg = NULL;
        mach_port_t livePort = MACH_PORT_NULL;
#elif DEPLOYMENT_TARGET_WINDOWS
        HANDLE livePort = NULL;
        Boolean windowsMessageReceived = false;
#endif
    __CFPortSet waitSet = rlm->_portSet;

        __CFRunLoopUnsetIgnoreWakeUps(rl);

#pragma mark - kCFRunLoopBeforeTimers   即将处理Timers
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
#pragma mark - kCFRunLoopBeforeSources  即将处理Sources
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
#pragma mark - __CFRunLoopDoBlocks  处理Block
    __CFRunLoopDoBlocks(rl, rlm);
#pragma mark - __CFRunLoopDoSources0  处理BlockSources0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
#pragma mark - __CFRunLoopDoBlocks  处理Block
            __CFRunLoopDoBlocks(rl, rlm);
    }

        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            msg = (mach_msg_header_t *)msg_buffer;
#pragma mark - 判断有无Sources1
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
#pragma mark - 如果有Sources1,跳转到handle_msg
                goto handle_msg;
            }
#elif DEPLOYMENT_TARGET_WINDOWS
            if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
                goto handle_msg;
            }
#endif
        }

        didDispatchPortLastTime = false;
#pragma mark - kCFRunLoopBeforeWaiting  即将进入休眠
    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();

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        do {
            if (kCFUseCollectableAllocator) {
                // objc_clear_stack(0);
                // 
                memset(msg_buffer, 0, sizeof(msg_buffer));
            }
            msg = (mach_msg_header_t *)msg_buffer;
#pragma mark - 等待别的消息唤醒当前线程
            __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);
#else
        if (kCFUseCollectableAllocator) {
            // objc_clear_stack(0);
            // 
            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);
#endif
        
        
#elif DEPLOYMENT_TARGET_WINDOWS
        // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
        __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
#endif
        
        __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);
#pragma mark - kCFRunLoopAfterWaiting  结束休眠,即将被唤醒
    if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);


#pragma mark - handle_msg
        handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);

#if DEPLOYMENT_TARGET_WINDOWS
        if (windowsMessageReceived) {
            // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
            __CFRunLoopModeUnlock(rlm);
        __CFRunLoopUnlock(rl);

            if (rlm->_msgPump) {
                rlm->_msgPump();
            } else {
                MSG msg;
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
            
            __CFRunLoopLock(rl);
        __CFRunLoopModeLock(rlm);
        sourceHandledThisLoop = true;
            
            // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced
            // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later.
            // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
            __CFRunLoopSetSleeping(rl);
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            
            __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL);
            
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);            
            __CFRunLoopUnsetSleeping(rl);
            // If we have a new live port then it will be handled below as normal
        }
        
        
#endif
        if (MACH_PORT_NULL == livePort) {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
        } else if (livePort == rl->_wakeUpPort) {
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            // do nothing on Mac OS
#if DEPLOYMENT_TARGET_WINDOWS
            // Always reset the wake up port, or risk spinning forever
            ResetEvent(rl->_wakeUpPort);
#endif
        }
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
#pragma mark - 被Timer唤醒
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer, because we apparently fired early
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
#if USE_MK_TIMER_TOO
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled.
            // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754
#pragma mark - 被Timer唤醒
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
#pragma mark - 被GCD唤醒
        else if (livePort == dispatchPort) {
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
#if DEPLOYMENT_TARGET_WINDOWS
            void *msg = 0;
#endif
#pragma mark - 被GCD唤醒,调用__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;
            didDispatchPortLastTime = true;
        } else {
#pragma mark - 被Source1唤醒
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            
            // If we received a voucher from this mach_msg, then put a copy of the new voucher into TSD. CFMachPortBoost will look in the TSD for the voucher. By using the value in the TSD we tie the CFMachPortBoost to this received mach_msg explicitly without a chance for anything in between the two pieces of code to set the voucher again.
            voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);

            // Despite the name, this works for windows handles as well
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        mach_msg_header_t *reply = NULL;
        sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
        if (NULL != reply) {
            (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
            CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
        }
#elif DEPLOYMENT_TARGET_WINDOWS
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
        }
            
            // Restore the previous voucher
            _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release);
            
        } 
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif

#pragma mark - 处理Blocks
    __CFRunLoopDoBlocks(rl, rlm);
        
#pragma mark - 计算retVal结果
    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;
    }
        
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        voucher_mach_msg_revert(voucherState);
        os_release(voucherCopy);
#endif

    } while (0 == retVal);

    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    return retVal;
}

你可能感兴趣的:(RunLoop)