RunLoop实现学习记录

注:以Core Foundation的实现作为参考(版本为CF-855.17)。

1. 相关数据结构

1.1 run loop的基本结构

CFRunLoopRef,是__CFRunLoop的结构体指针:

typedef struct __CFRunLoop * CFRunLoopRef;

struct __CFRunLoop {
    CFRuntimeBase _base; // CF对象类型,相当于OC对象的isa
    
    pthread_mutex_t _lock; // 访问mode列表时保证同步
    __CFPort _wakeUpPort; // 线程被唤醒时使用 
    Boolean _unused;
    volatile _per_run_data *_perRunData; // 每次运行的相关状态数据
    pthread_t _pthread; // 绑定的线程
    uint32_t _winthread;
    
    
    // 标记为“common”的mode的名称组成的集合
    CFMutableSetRef _commonModes;
    // “common”的mode对应的统一item的集合
    CFMutableSetRef _commonModeItems;
    // runloop当前所处的mode
    CFRunLoopModeRef _currentMode;
    // 所有的mode的集合
    CFMutableSetRef _modes;
    
    
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

作为RunLoop的基本数据结构,CFRunLoopRef内部主要包含了:

  • _commonModes:标记为“common”的mode的集合
  • _commonModeItems:所有“common”mode中共享的item,即修改此内部的item,就会对所有的“common”的mode内部的item进行更新。
  • _currentMode:当前所在的mode模式,切换时要退出重新指定
  • _modes:所有支持的mode。

常用API:

// 获取当前线程的run loop(延迟加载,不访问不创建)
CF_EXPORT CFRunLoopRef CFRunLoopGetCurrent(void);

// 获取主线程的run loop(APP启动后自动创建并运行)
CF_EXPORT CFRunLoopRef CFRunLoopGetMain(void);

既然引入了mode这一概念,我们就看看mode到底是个啥。

1.2 RunLoop的Mode

RunLoop的mode为内部数据,没有对外公开。类型为CFRunLoopModeRef,即__CFRunLoopMode的结构体指针。看一下简化过的代码结构:

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock; // 在对mode加锁之前必须持有了run loop的锁
    
    
    CFStringRef _name; // mode名称,区分mode使用
    Boolean _stopped;
    char _padding[3];
    
    
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    
    
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;

    mach_port_t _timerPort;
    Boolean _mkTimerArmed;

    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

其中,CFRunLoopModeRef中主要包含了如下几个内容:

  • _name:用于识别区分不同的mode实例。切换mode时可以使用。
  • _sources0:非基于端口的源的集合(我们在线程中自己添加的一般都是source0)
  • _sources1:基于端口的源的集合(通过Mach Port进行线程间通信使用)
  • _observers:观察者的数组,用于runloop在不同状态时发送通知的接收对象。
  • _timers:定时器数组,包含的就是NSTimer对象。

常用API:

// 启动run loop
CF_EXPORT void CFRunLoopRun(void);

// 以指定mode的方式启动run loop
CF_EXPORT SInt32 CFRunLoopRunInMode(CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);

// 将指定mode加入到run loop的commonMode数组中(加入的是modeName)
CF_EXPORT void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef mode);

下面我们分着来看各部分的数据结构。

1.3 RunLoop源

CFRunLoopSourceRef,即__CFRunLoopSource结构体的指针。其基本结构如下:

typedef struct __CFRunLoopSource * CFRunLoopSourceRef;

struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order; // 优先级,不可变
    CFMutableBagRef _runLoops; // 绑定的runloop对象
    union {
        CFRunLoopSourceContext version0;    /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1;   /* immutable, except invalidation */
    } _context;
};

其中,联合体中的成员version0和version1即为上面mode中的source0和source1。大概看一下二者的结构体:

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
    void    (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*perform)(void *info);
} CFRunLoopSourceContext;


typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
    mach_port_t (*getPort)(void *info);
    void *  (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
    void *  (*getPort)(void *info);
    void    (*perform)(void *info);
#endif
} CFRunLoopSourceContext1;

可以看到,二者内容基本相同,都是包含了内存管理、相等性、hash等信息。不同的是source1的结构体(CFRunLoopSourceContext1)包含了mach_port相关函数。

因此,可以将source根据类型(version成员的0和1)分为source0和source1。

常用API:

// 创建source(传入不同类型的context即可得到source0或source1)
CF_EXPORT CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context);

// 向run loop的指定mode中添加source
CF_EXPORT void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);

// 从run loop的指定mode中移除source
CF_EXPORT void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
1.4 Run Loop的定时器

定时器为CFRunLoopTimerRef类型,也就是__CFRunLoopTimer的结构体指针。

typedef struct __CFRunLoopTimer * CFRunLoopTimerRef;

struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop; // 绑定的run loop
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate; // 下次触发时间
    CFTimeInterval _interval; // 时间间隔
    CFTimeInterval _tolerance; // 宽容度
    uint64_t _fireTSR;          /* TSR units */
    CFIndex _order;         /* immutable */
    CFRunLoopTimerCallBack _callout; // 回调
    CFRunLoopTimerContext _context; /* immutable, except invalidation */
};

其中,主要包含了:

  • _runLoop:绑定到的run loop
  • _interval:执行事件间隔(重复执行的定时器)
  • _tolerance:宽容度,也就是指定执行时间的最大延迟时间
  • _callout:执行的回调函数指针
  • _context:定时器上下文对象,包含内存管理等相关函数
typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
} CFRunLoopTimerContext;

typedef void (*CFRunLoopTimerCallBack)(CFRunLoopTimerRef timer, void *info);

常用API:

// 以callback回调函数指针的形式创建timer
CF_EXPORT CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, CFRunLoopTimerCallBack callout, CFRunLoopTimerContext *context);

// 以block的形式创建timer
CF_EXPORT CFRunLoopTimerRef CFRunLoopTimerCreateWithHandler(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, void (^block) (CFRunLoopTimerRef timer)) CF_AVAILABLE(10_7, 5_0);

// 向run loop的指定mode中添加timer
CF_EXPORT void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

// 从run loop的指定mode中移除timer
CF_EXPORT void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
1.5 Run Loop的观察者

观察者为CFRunLoopObserverRef类型,即__CFRunLoopObserver的结构体指针。

typedef struct __CFRunLoopObserver * 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 */
};

其中,主要包含:

  • _runLoop:对应的run loop
  • _activities:用于监听的run loop的指定运行状态。
  • _order:优先级
  • _callout:收到监听通知的回调函数指针。
  • _context:上下文对象

activities,即CFOptionFlags,用于识别run loop在不同执行时刻的运行状态:

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    /** 进入loop */
    kCFRunLoopEntry = (1UL << 0),
    /** 准备执行timer */
    kCFRunLoopBeforeTimers = (1UL << 1),
    /** 准备执行source */
    kCFRunLoopBeforeSources = (1UL << 2),
    /** 线程准备进入休眠 */
    kCFRunLoopBeforeWaiting = (1UL << 5),
    /** 线程刚被唤醒,还没有进行任务处理 */
    kCFRunLoopAfterWaiting = (1UL << 6),
    /** 退出loop */
    kCFRunLoopExit = (1UL << 7),
    
    /** 所有的状态 */
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

上下文对象为CFRunLoopObserverContext结构体,内部包含了内存管理的相关函数指针:

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
} CFRunLoopObserverContext;

回调函数的类型为:

typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);

常用API:

// 创建新的run loop观察者,使用callback回调函数指针的方式
CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context);

// 创建新的run loop观察者,使用block的方式
CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreateWithHandler(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, void (^block) (CFRunLoopObserverRef observer, CFRunLoopActivity activity)) CF_AVAILABLE(10_7, 5_0);

// 向run loop的指定mode中添加观察者
CF_EXPORT void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode);

// 从run loop的指定mode中移除观察者
CF_EXPORT void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode);

2. 重点API基本实现

2.1 CFRunLoopGetMain & CFRunLoopGetCurrent

系统隐藏了生成过程,通过获取线程对应的runloop对象,即可完成创建。

// 获取主线程的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
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}

可以看到,真正的实现都在 _CFRunLoopGet0 函数中:

static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFSpinLock_t loopsLock = CFSpinLockInit;

CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    // t为0,即为主线程
    if (pthread_equal(t, kNilPthreadT)) {
        t = pthread_main_thread_np();
    }
    // 获取锁
    __CFSpinLock(&loopsLock);
    
    // 懒加载,第一次调用为主线程,创建全局字典,并将新创建的主线程runloop加入其中
    if (!__CFRunLoops) {
        // 释放锁
        __CFSpinUnlock(&loopsLock);
        
        // 创建字典容器,用于存储runloop
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        
        // 创建主线程的runloop
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        // 插入到字典中(key为线程指针)
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        
        // __CFRunLoops全局字典指针指向此字典容器
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
            CFRelease(dict);
        }
        CFRelease(mainLoop);
        
        // 再次获取锁
        __CFSpinLock(&loopsLock);
    }
    
    
    // 从全局字典容器中获取指定runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    // 释放锁
    __CFSpinUnlock(&loopsLock);
    
    // 懒加载
    if (!loop) {
        // 创建新runloop
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        
        __CFSpinLock(&loopsLock);
        
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        if (!loop) {
            // 在全局字典容器中插入runloop
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            loop = newLoop;
        }
        __CFSpinUnlock(&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;
}

通过实现可以看到:

  • runloop对象整体懒加载,不调用不生成。
  • runloop对象保存在全局字典中,key为线程指针。
  • 系统第一次调用时,生成全局字典,且自动创建主线程的runloop,插入其中,因此APP可以保持运行。
2.2 CFRunLoopRun

首先看一下run loop的启动过程:

void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}

在以上两个启动run loop的函数中,真正的实现都包含在CFRunLoopRunSpecific中,且此函数会返回run loop的执行状态(由CFRunLoopRunInMode()函数返回)。

enum {
    kCFRunLoopRunFinished = 1,
    kCFRunLoopRunStopped = 2,
    kCFRunLoopRunTimedOut = 3,
    kCFRunLoopRunHandledSource = 4
};

现在,我们看一下运行循环,到底是如何循环的。

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    
    // 正在释放,直接返回
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    
    // run loop获取锁
    __CFRunLoopLock(rl);
    
    // 根据指定modeName,获取到mode对象
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    
    // 若不存在此mode,或内部没有mode item
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
        // 返回
        Boolean did = false;
        if (currentMode) __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopUnlock(rl);
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    
    // 设置新的perRunData,返回旧值
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    
    // 之前所处的mode
    CFRunLoopModeRef previousMode = rl->_currentMode;
    
    // 记录为新的运行mode
    rl->_currentMode = currentMode;
    
    // 设置为已完成,待返回
    int32_t result = kCFRunLoopRunFinished;

    // 如果存在监听进入loop状态的观察者
    if (currentMode->_observerMask & kCFRunLoopEntry )
        // 通知观察者,run loop已进入(新mode进入)
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    // ** 核心:切换运行新mode(循环在这里面实现) **
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    // 如果存在监听退出loop状态的观察者
    if (currentMode->_observerMask & kCFRunLoopExit )
        // 通知观察者,run loop已退出
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    // 释放mode的锁
    __CFRunLoopModeUnlock(currentMode);
    
    // 恢复为原始的perRunData,恢复状态
    __CFRunLoopPopPerRunData(rl, previousPerRun);
    
    // run loop的运行mode恢复为原mode
    rl->_currentMode = previousMode;
    
    // 释放run loop自身的锁
    __CFRunLoopUnlock(rl);
    
    return result;
}

CFRunLoopRunSpecific函数的整体运行,是一个“创建新现场 -- 执行loop -- 恢复旧现场”的过程。

  1. 根据指定的modeName名称,获取run loop内部保存的mode对象。将其设置为currentMode,然后创建新的perRunData进行设置。且如果存在进入循环的观察者,发通知进行告知。

其中,_per_run_data指针标记为volatile,即告知编译器要直接从原地址读取值,而不是从寄存器中读取(防止多线程更改时不修改寄存器中的值导致的数据不一致问题)。__CFRunLoopPushPerRunData函数的内部实现为:

typedef struct _per_run_data {
    uint32_t a;
    uint32_t b;
    uint32_t stopped;
    uint32_t ignoreWakeUps;
} _per_run_data;

CF_INLINE volatile _per_run_data *__CFRunLoopPushPerRunData(CFRunLoopRef rl) {
    volatile _per_run_data *previous = rl->_perRunData;
    rl->_perRunData = (volatile _per_run_data *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(_per_run_data), 0);
    rl->_perRunData->a = 0x4346524C;
    rl->_perRunData->b = 0x4346524C; // 'CFRL'
    rl->_perRunData->stopped = 0x00000000;
    rl->_perRunData->ignoreWakeUps = 0x00000000;
    return previous;
}

可以看到,runloop的_perRunData成员每次都是创建固定的新值。用来管理runloop的相关设置(stop状态,是否允许唤醒等)

  1. 调用 __CFRunLoopRun 函数,此函数才是真正的运行循环过程。后面进行单独说明。
  2. 运行循环执行结束后,恢复现场。将上一次的运行mode、_perRunData等数据进行恢复。且如果存在监听退出状态的观察者,发通知告知。

这里,我们看一下runloop是如何给观察者发送通知的。

2.2 __CFRunLoopDoObservers
static void __CFRunLoopDoObservers() __attribute__((noinline));
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */
    CHECK_FOR_FORK();

    // 查看mode中observers的个数
    CFIndex cnt = rlm->_observers ? CFArrayGetCount(rlm->_observers) : 0;
    // 不存在,直接返回
    if (cnt < 1) return;

    /* Fire the observers */
    STACK_BUFFER_DECL(CFRunLoopObserverRef, buffer, (cnt <= 1024) ? cnt : 1);
    // cnt超过1024后,开辟cnt个内存空间,相当于数组
    CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef *)malloc(cnt * sizeof(CFRunLoopObserverRef));
    CFIndex obs_cnt = 0;
    for (CFIndex idx = 0; idx < cnt; idx++) {
        // 依次取出observer
        CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
        // 若observer中存在需要的activity
        if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)) {
            // retain后,插入数组中
            collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo);
        }
    }
    
    // 释放mode的锁
    __CFRunLoopModeUnlock(rlm);
    // 释放run loop锁
    __CFRunLoopUnlock(rl);
    
    // **** 由此可以看出,为了提高访问效率,将所有的观察者添加到额外的数组中,是为了释放锁后,其他对象的高效访问。 ****
    
    
    for (CFIndex idx = 0; idx < obs_cnt; idx++) {
        // 从数组中依次取出observer
        CFRunLoopObserverRef rlo = collectedObservers[idx];
        // 对observer加锁
        __CFRunLoopObserverLock(rlo);
        
        if (__CFIsValid(rlo)) {
            // 根据是否重复确定是否需要置为无效
            Boolean doInvalidate = !__CFRunLoopObserverRepeats(rlo);
            // 设置对应位的值(标记为正在调用)
            __CFRunLoopObserverSetFiring(rlo);
            // 释放锁
            __CFRunLoopObserverUnlock(rlo);
            
           // 调用观察者对应的回调方法(_callout)
            __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);
            
            // 需要置为无效,则释放此observer
            if (doInvalidate) {
                CFRunLoopObserverInvalidate(rlo);
            }
            
            // 设置对应位的值(标记为没有调用)
            __CFRunLoopObserverUnsetFiring(rlo);
        } else {
            // 观察者对象错误,直接释放锁
            __CFRunLoopObserverUnlock(rlo);
        }
        
        // 释放observer(数组不再保留observer)
        CFRelease(rlo);
    }
    // 释放runloop和mode的锁
    __CFRunLoopLock(rl);
    __CFRunLoopModeLock(rlm);

    // 创建了新数组,则释放内存
    if (collectedObservers != buffer) free(collectedObservers);
}
  1. 将指定run loop mode中的_observers取出,单独放到一个指定数组中进行处理(超出1024个observer后要单独开辟内存)。由于调用callback函数为同步操作,由可能会长时间锁住runloop和mode。故单独处理,防止影响runloop的执行效率。
  2. 将监听的activity对应的observer插入到数组容器中。activity即上面说过的CFOptionFlags,也就是runloop运行过程中的各种监控时间点。
  3. 插入到数组容器时要对observer进行保留操作,使用后要对observer进行释放操作,内存管理要时刻谨记。
  4. 在observer数组中依次取出,通过 CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION 函数对observer的_callout进行函数调用。
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    if (func) {
        func(observer, activity, info);
    }
    getpid(); // thwart tail-call optimization
}

主要就是直接进行函数调用(getpid神马进程相关的,就不动了。。。)。

2.3 __CFRunLoopRun

前面说了,__CFRunLoopRun个才是核心所在,真正的“loop”在这里执行。

由于代码过长,我们分步骤看。

2.3.1 其他(非do-while循环)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {

    // 1. 状态判断
    if (__CFRunLoopIsStopped(rl)) {
        // 如果runloop已经stop(_perRunData中的stop的值)
        // 恢复stop为初值,返回已stop状态
        __CFRunLoopUnsetStopped(rl);
        return kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
        // 若当前mode已经stop
        // 恢复stop为初值,返回已stop状态
        rlm->_stopped = false;
        return kCFRunLoopRunStopped;
    }
    
    // 2. dispatchPort的获取
    
    // 派发端口,如果当前runloop是主线程runloop,则为主队列port,否则为NULL
    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();
    }
    
    
    // 3. 超时定时器的设置
    
    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) {
        // 有超时,但是未达到上限的情况
        
        // 通过global队列,添加一个超时的timer,到时间唤醒线程,释放runloop
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT);
        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;
    }
    
    
    
    int32_t retVal = 0;
    do {
        ...
    } while (0 == retVal);
    
    // retVal只要不为0,则runloop结束循环,退出



    if (timeout_timer) {
        // 存在超时定时器,移除
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    return retVal;
}

非核心部分主要做了三件事:

  1. 对runloop和currentMode的运行状态进行判断,防止无畏的运行。
  2. 根据情况,查看端口是否为GCD的主队列端口(用于后面通过主线程自身端口调用mach_msg函数,执行主队列的异步任务)。
  3. 根据设置的超时时间,使用GCD添加一个定时器,超时时直接释放runloop。最后结尾时检查移除此定时器。
2.3.2 do-while运行循环
    int32_t retVal = 0;
    do {
        // 1. 根据运行状态,进行预处理(发通知、执行block、准备处理GCD事件、休眠)

        // 存在kCFRunLoopBeforeTimers的观察者,发通知
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        // 存在kCFRunLoopBeforeSources的观察者,发通知
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

        // 调用block的时机1
        __CFRunLoopDoBlocks(rl, rlm);

        // 执行source0(手动source)
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            // 执行source0后,调用block(调用block的时机2)
            __CFRunLoopDoBlocks(rl, rlm);
        }

        // GCD的主队列
        if (MACH_PORT_NULL != dispatchPort) {
            msg = (mach_msg_header_t *)msg_buffer;
            // 通过主队列端口直接发送消息(内部的source1消息,mach_msg执行),进行处理
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
                goto handle_msg;
            }
        }

        // 存在监听kCFRunLoopBeforeWaiting的观察者,发通知
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        
        
        msg = (mach_msg_header_t *)msg_buffer;
        // 尝试通过mach_msg函数进入休眠状态,接收到消息后(即被唤醒),返回true
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
                
        
        // 如果存在监听kCFRunLoopAfterWaiting的观察者,发通知
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);



        // 2. 处理消息部分(线程已被唤醒 或 直接进入处理)
        
        // ** 处理消息:**       
        handle_msg:;

        if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            // timer唤醒的
            // 以当前时间为时间点,执行相应的timer
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        } else if (livePort == dispatchPort) {
            // GCD主队列唤醒的(dispatchPort不为NULL,即证明是主队列唤醒的)
            // 执行主队列的任务block
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);            
        } else {
            // 其他线程的source1唤醒的
            // 根据livePort,查询获取source1对象
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
                mach_msg_header_t *reply = NULL;
                // 执行source1
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
            }
        } 
        
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
        
        // 执行block的时机3
        __CFRunLoopDoBlocks(rl, rlm);
        
        
        // 3. 根据状态设置retVal值,决定运行循环是否继续
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            // 单次执行完source
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            // runloop超时
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            // runloop已停止
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            // runloop的对应mode已停止
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            // runloop执行完成
            retVal = kCFRunLoopRunFinished;
        }
        
    } while (0 == retVal);

虽然代码很多(已经大幅度简化),但实际上只是分为三部分:

  1. 根据运行状态,进行预处理:
    • 在不同运行状态,给观察者发通知(准备执行timer、准备执行source)
    • 执行block
    • 执行配置到mode中的source0,成功后再次检测执行block。实现函数为 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
    • 准备处理GCD事件(通过mach_msg函数,自身主队列的port执行,存在则直接去处理任务)
    • 没有其他事件时,通过mach_msg函数,让线程进入睡眠,同时在接收消息后自动唤醒
  2. 处理消息部分(线程已被唤醒 或 直接进入处理),根据端口类型,对执行的工作进行区分:
    • 执行timer:CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION,其入口函数 __CFRunLoopDoTimers__CFRunLoopDoObservers 实现相似:先将符合要求(刚刚到达或者稍微过时一点的)的timer加入到数组中,防止长时间锁住runloop,然后依次调用 __CFRunLoopDoTimer ,也就是calling_out函数,完成回调。
    • 执行GCD主队列的异步任务:CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
    • source1的主动唤醒任务:mach_msg,进入内核态,直接进行线程通信
    • 再次检测执行block
  3. 根据状态设置retVal值,决定运行循环是否继续。如果状态都不满足,仍为0,则继续执行循环,保证线程不退出。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容