前言

一、什么是Runloop,以及它的作用

二、NSRunloop和CFRunLoopRef的区别

三、Runloop的底层结构

四、分析Runloop和线程的关系

五、Runloop的Mode

六、Timer的添加流程分析

七、Runloop的工作流程

八、Source0和Source1

九、 Observer


在多线程的总结中,提到了不少有关于Runloop的地方,今天就来具体的总结一下Runloop的相关知识点。


一、什么是Runloop,以及它的作用

Runloop简单的来说就是运行循环,内部是一个do-while循环。它保证了程序的持续运行。是一种保障线程循环处理事件而不会退出的机制。
它主要有以下3个作用:

(1)保持程序的持续运行。

(2)处理App中各种事件(触摸、手势、定时器)。

(3)节省cpu的资源、提高程序的性能,该休息的时候休息、该工作的时候工作。


二、NSRunloop和CFRunLoopRef的区别

首先NSRunLoop是基于CoreFoundation中CFRunLoopRef的一层封装,这个IOS开发者都知道。
重点是NSRunLoop是非线程安全的,而CFRunLoopRef是线程安全的。

为什么NSRunLoop不是线程安全呢?

线程安全是指:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。当多线程去读取同一块资源的时候是线程安全的,但是当去修改同一块资源的时候就可能会出现数据错乱的情况了。
CFRunLoopRef提供的API中,操作Runloop的函数都加了一把互斥锁。

CF_INLINE void __CFRunLoopLock(CFRunLoopRef rl) {
    pthread_mutex_lock(&(((CFRunLoopRef)rl)->_lock));

所以它是线程安全的。
而NSRunLoop官方文档明确写道,它是非线程安全的:

另外通过GNUStep的源码去查看它的方法内部实现都没有加锁,所以它是非线程安全的。

三、Runloop的底层结构

首先分析一下Runloop的结构,通过CFRunLoop的源码来具体分析。

typedef struct __CFRunLoop * CFRunLoopRef;

__CFRunLoop是c的结构体,其中重要的成员如下:

struct __CFRunLoop {
    .......
    pthread_mutex_t _lock;			/* locked for accessing mode list */
    pthread_t _pthread; //线程
    uint32_t _winthread; //windows平台使用
    CFMutableSetRef _commonModes; //UITrackingRunLoopMode/kCFRunLoopDefaultMode
    CFMutableSetRef _commonModeItems; //observer/source/timer
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes; //内置的modes
    .............
};

核心成员:

_lock:用来保证访问mode list的线程安全
_pthread:每一个RunLoop都对应一条线程
_commonModes:将需要同时处理事件的多个Mode(包括UITrackingRunLoopMode和kCFRunLoopDefaultMode等)放入_commonModes集合进行标记。
_commonModeItems:对应observer/source/timer
_currentMode:当前的模式
_modes:内置的模式

重点:_commonModeItems 与 _commonModes是kCFRunLoopCommonModes (NSRunLoopCommonModes)实现逻辑。比如当我们添加一个timer的时候,指定的模式为NSRunLoopCommonModes,此时timer会被添加到_commonModeItems中,并将timer追加到_commonModes中已经标记的模式下。


四、分析Runloop和线程的关系

Runloop的核心成员中有一个_pthread,也就是每一个Runloop都会有唯一对应的线程。
那么线程和Runloop是如何建立联系的呢?

1、通过CFRunLoop的Api函数:CFRunLoopGetCurrent(获取当前的Runloop)和CFRunLoopGetMain(获取主线程的Runloop)来分析:

CFRunLoopRef CFRunLoopGetMain(void) {
    //进程检查
    CHECK_FOR_FORK();
    //保存在常量区
    static CFRunLoopRef __main = NULL; // no retain needed
    //判断__main是否存在,如果不存在执行_CFRunLoopGet0
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed(不需要原子性)
    return __main;
}

CFRunLoopRef CFRunLoopGetCurrent(void) {
    //进程检查
    CHECK_FOR_FORK();
    //通过线程的私有数据获取对应的Runloop
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    //如果有就返回RunLoop
    if (rl) return rl;
    //如果没有执行_CFRunLoopGet0
    return _CFRunLoopGet0(pthread_self());
}

在分析_CFRunLoopGet0之前,先看下TSD是什么。

2、TSD (Thread-specific data)相关: 源码

Thread-specific data是线程私有数据,RunLoop会保存在线程的私有数据里面。

typedef struct __CFTSDTable {
    //记录destructors数组元素的个数
    uint32_t destructorCount;
    //数组用来保存私有数据
    uintptr_t data[CF_TSD_MAX_SLOTS];
    //用来保存释放函数
    tsdDestructor destructors[CF_TSD_MAX_SLOTS];
} __CFTSDTable;

// For the use of CF and Foundation only
//是获取__CFTSDTable的data数据,并返回slot的值
CF_EXPORT void *_CFGetTSD(uint32_t slot) {
    if (slot > CF_TSD_MAX_SLOTS) {
        _CFLogSimple(kCFLogLevelError, "Error: TSD slot %d out of range (get)", slot);
        HALT;
    }
    __CFTSDTable *table = __CFTSDGetTable();
    if (!table) {
        _CFLogSimple(kCFLogLevelWarning, "Warning: TSD slot %d retrieved but the thread data has already been torn down.", slot);
        return NULL;
    }
    uintptr_t *slots = (uintptr_t *)(table->data);
    return (void *)slots[slot];
}

// For the use of CF and Foundation only
//给__CFTSDTable里设置data[slot]和destructors[slot]位置的值。
CF_EXPORT void *_CFSetTSD(uint32_t slot, void *newVal, tsdDestructor destructor) {
    if (slot > CF_TSD_MAX_SLOTS) {
        _CFLogSimple(kCFLogLevelError, "Error: TSD slot %d out of range (set)", slot);
        HALT;
    }
    __CFTSDTable *table = __CFTSDGetTable();
    if (!table) {
        _CFLogSimple(kCFLogLevelWarning, "Warning: TSD slot %d set but the thread data has already been torn down.", slot);
        return NULL;
    }
    void *oldVal = (void *)table->data[slot];
    table->data[slot] = (uintptr_t)newVal;
    table->destructors[slot] = destructor;
    return oldVal;
}

3、_CFRunLoopGet0分析

//全局字典
static CFMutableDictionaryRef __CFRunLoops = NULL;
//互斥锁
static CFLock_t loopsLock = CFLockInit;

// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    //判断传入的线程是否为空
    if (pthread_equal(t, kNilPthreadT)) {
        //如果为空默认主线程
        t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    //判断全局字典是否为空
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
        //如果为空,创建临时字典
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        //创建runloop
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        //以线程地址为key,runloop为value,放入临时字典
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        //将这个临时字典复制到全局字典中
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
            CFRelease(dict);
        }
        CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    //此时全局字典肯定有值,根据当前线程来获取对应的Runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    if (!loop) {
        //如果loop没找到,那么创建一个
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
        //再获取一次
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        if (!loop) {
            //如果loop仍然不存在,将新创建的newLoop存入全局字典中
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            loop = newLoop;
        }
            // 不要在 loopsLock 中释放运行循环,因为 CFRunLoopDeallocate 可能最终会占用它
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFUnlock(&loopsLock);
        CFRelease(newLoop);
    }
    //判断t是否是当前线程,如果是当前线程,将runloop放入线程私有数据中
    if (pthread_equal(t, pthread_self())) {
        // `CFInternal.h`定了枚举`__CFTSDKeyRunLoop` = 10 与 `__CFTSDKeyRunLoopCntr` = 11
        // 如果线程TSD,枚举`__CFTSDKeyRunLoopCntr` 对应`slot`未存值
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
            //为其存值`PTHREAD_DESTRUCTOR_ITERATIONS-1`,并设置析构函数`__CFFinalizeRunLoop`,
            //__CFFinalizeRunLoop 释放函数 指针,为了当线程销毁的来调用退出runloop
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    return loop;
}

//线程销毁时,会销毁线程对应的TSD
static void __CFTSDFinalize(void *arg) {
    //获取私有数据表
    __CFTSDTable *table = (__CFTSDTable *)arg;
    //遍历所有slot 比如存RunLoop的__CFTSDKeyRunLoop以及__CFTSDKeyRunLoopCntr
    for (int32_t i = 0; i < CF_TSD_MAX_SLOTS; i++) {
        if (table->data[i] && table->destructors[i]) {
            uintptr_t old = table->data[i];
            table->data[i] = (uintptr_t)NULL;
           //当遍历到__CFTSDKeyRunLoopCntr时,执行释放runloop的释放函数
            table->destructors[i]((void *)(old));
        }
    }
    if (table->destructorCount == PTHREAD_DESTRUCTOR_ITERATIONS - 1) {
        free(table);
        __CFTSDSetSpecific(CF_TSD_BAD_PTR);
        return;
    }
}

主要流程是:
(1)判断传入的线程是否为空,如果为空默认设置为主线程
(2)判断全局字典是否为空,如果为空,创建主线程的Runloop,并添加到临时字典中(以线程地址为key,runloop为value),然后将这个临时字典复制到全局字典中。
(3)从全局字典获取对应线程的RunLoop,如果未找到,就创建一个。
(4)判断传入的线程是否是当前线程,如果是则把当前的RunLoop和释放函数指针保存到线程的私有数据中。
(5)最后返回runloop。

4、RunLoop的创建和销毁

创建Runloop:

static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
    CFRunLoopRef loop = NULL;
    CFRunLoopModeRef rlm;
    //计算runloop的空间
    uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
    //通过_CFRuntimeCreateInstance创建runloop
    loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
    if (NULL == loop) {
	return NULL;
    }
    //设置loop的状态数据
    (void)__CFRunLoopPushPerRunData(loop);
    __CFRunLoopLockInit(&loop->_lock);
    loop->_wakeUpPort = __CFPortAllocate();
    if (CFPORT_NULL == loop->_wakeUpPort) HALT;
    __CFRunLoopSetIgnoreWakeUps(loop);
    //设置loop的成员属性
    loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
    CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
    loop->_commonModeItems = NULL;
    loop->_currentMode = NULL;
    loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
    loop->_blocks_head = NULL;
    loop->_blocks_tail = NULL;
    loop->_counterpart = NULL;
    loop->_pthread = t;
#if DEPLOYMENT_TARGET_WINDOWS
    loop->_winthread = GetCurrentThreadId();
#else
    loop->_winthread = 0;
#endif
    //查找mode,如果没有就创建
    rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
    if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
    return loop;
}

其创建过程总结如下:
(1)通过_CFRuntimeCreateInstance创建一个RunLoop对象
(2)设置Runloop的属性,以及通过调用__CFRunLoopFindMode来创建模式并添加到modes集合中。

释放Runloop:

CF_PRIVATE void __CFFinalizeRunLoop(uintptr_t data) {
    CFRunLoopRef rl = NULL;
    if (data <= 1) {
        __CFLock(&loopsLock);
        //移除全局字典中线程对应的runloop
        if (__CFRunLoops) {
            rl = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(pthread_self()));
            if (rl) CFRetain(rl);
            CFDictionaryRemoveValue(__CFRunLoops, pthreadPointer(pthread_self()));
        }
        __CFUnlock(&loopsLock);
    } else {
        _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(data - 1), (void (*)(void *))__CFFinalizeRunLoop);
    }
    if (rl && CFRunLoopGetMain() != rl) { // protect against cooperative threads
        if (NULL != rl->_counterpart) {
            CFRelease(rl->_counterpart);
            rl->_counterpart = NULL;
        }
	// purge all sources before deallocation
        //清除所有mode下的事件
        CFArrayRef array = CFRunLoopCopyAllModes(rl);
        for (CFIndex idx = CFArrayGetCount(array); idx--;) {
            CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
            __CFRunLoopRemoveAllSources(rl, modeName);
        }
        __CFRunLoopRemoveAllSources(rl, kCFRunLoopCommonModes);
        CFRelease(array);
    }
    //释放runloop
    if (rl) CFRelease(rl);
}

主要流程如下:
(1)线程退出时会调用__CFFinalizeRunLoop。
(2)移除全局字典中当前应线程的runloop。
(3)移除所有mode下的事件源。
(4)释放runloop

5、程序启动的时候会默认创建主线程的Runloop

了解了Runloop的创建和释放流程之后,来到main函数

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    //第三个参数:NSString * _Nullable principalClassName 默认是UIApplication
    // If nil is specified for principalClassName, the value for NSPrincipalClass from the Info.plist is used. If there is no
    // NSPrincipalClass key specified, the UIApplication class is used. The delegate class will be instantiated using init.
    // 如果 principalClassName 指定为 nil,则使用 Info.plist 中 NSPrincipalClass 的值。  // 如果没有指定 NSPrincipalClass 键,则使用 UIApplication 类。 委托类将使用 init 实例化。
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

main函数中,非常关键的方法是 " return UIApplicationMain(argc, argv, nil, appDelegateClassName)"
这里会return并执行UIApplicationMain方法。假如这里直接return返回了,那么程序就结束了。所以UIApplicationMain方法中必然会开启一个Runloop保持程序的运行。
我在AppDelegate入口下一个断点:

主线程的堆栈信息如下:

根据堆栈信息,可以确定UIApplicationMain方法中,开启了Runloop并运行。
我下一个符号断点“CFRunLoopGetMain”:

在UIApplicationInitialize方法中,首次去执行了CFRunLoopGetMain,创建了主线程的Runloop,并且将UITrackingRunLoopMode模式添加到CommonModes中。

总结:

(1)Runloop和线程是一一对应的关系

(2)程序在启动的时候,会默认开启主线程的Runloop。

(3)当线程销毁的时候,Runloop会自动销毁。


五、Runloop的Mode

1、关于RunLoop的mode,先来看一张图:


一个RunLoop中,包含多个Mode,通过上面分析的RunLoop的结构就可以知道。
一个Mode包含Source0、Source1、Timers、Observers(统称item)。

struct __CFRunLoopMode {
    .........
    CFStringRef _name;
    ...............
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
..................
};

__CFRunLoopMode同样是一个结构体。
每次运行RunLoop都需要指定一个Mode,该Mode会被设置为_currentMode,只有与该Mode关联的输入源source0、source1才能处理,同样监听runloop的运行,只有与该Mode关联的observers才能收到通知,以及与该Mode关联的Timer才会启动。
那么__CFRunLoopMode有几种类型呢?
常用的共有5种:
(1)KCFRunloopDefaultMode: 默认模式,主线程是在这个运行模式下运行的。
(2)UITrackingRunLoopMode: 跟踪用户交互事件
(3)UIInitializationRunLoopMode:在刚启动App时进入的一个Mode,启动完成就不在使用了。
(4)GSEventReceiveRunLoopMode:系统内部事件,通常用不到
(5)kCFRunLoopCommonModes: 伪模式,不是一种真正的运行模式,是同步Source/Timer/Observer到多个Mode中的一种解决方案。

2.自定义Mode

除了以上的5种模式,也可以自定义Mode。
但是自定义的模式中必须有关联的事件,否则自定义模式没有任何意义

......
@property (nonatomic, strong) NSTimer *timer;
........
static NSString * const kRunLoopCustomMode1 = @"CustomMode1";
static NSString * const kRunLoopCustomMode2 = @"CustomMode2";
.......
/// 自定义mode
- (void)func {
    [NSThread detachNewThreadWithBlock:^{
        self.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {
            NSLog(@"1");
        }];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:kRunLoopCustomMode1];
        [[NSRunLoop currentRunLoop] runMode:kRunLoopCustomMode1 beforeDate:[NSDate distantFuture]];
    }];
}

- (void)dealloc {
    [self.timer invalidate];
}

为了不防止主线程的运行,我们创建一个子线程,然后将timer加入到自定义的mode中并启动runloop在自定义的mode下运行。

注意:以上代码添加自定义mode没有问题,但是会造成循环引用。因为创建的子线程和子线程的runloop放在全局字典中,被全局字典所持有,然后线程又持有了self。self又持有了timer。就会形成:

thead(runloop) -> self -> timer(runloop)

当离开页面的时候,self需要等thread释放,thread需要等timer释放,runloop停止(runloop停止之后,线程执行完任务就会自动销毁,线程自动销毁之后,就会执行上面分析的__CFFinalizeRunLoop,移除全局字典中此线程对应的runloop),timer又要等self释放,也就形成了循环引用。所以为了打破循环引用,此时需要用__weak修饰当前self。

/// 自定义mode
- (void)func {
    __weak typeof(self)weakSelf = self;
    [NSThread detachNewThreadWithBlock:^{
        weakSelf.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {
            NSLog(@"1");
        }];
        [[NSRunLoop currentRunLoop] addTimer:weakSelf.timer forMode:kRunLoopCustomMode1];
        [[NSRunLoop currentRunLoop] runMode:kRunLoopCustomMode1 beforeDate:[NSDate distantFuture]];
    }];
}

我们也可以把自定义的mode加入到commonModes中

- (void)func1 {
    __weak typeof(self)weakSelf = self;
    [NSThread detachNewThreadWithBlock:^{
        weakSelf.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {
            NSLog(@"1");
        }];
        CFRunLoopAddCommonMode(CFRunLoopGetCurrent(), (__bridge CFStringRef)kRunLoopCustomMode1);
        [[NSRunLoop currentRunLoop] addTimer:weakSelf.timer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] runMode:kRunLoopCustomMode1 beforeDate:[NSDate distantFuture]];
    }];
}

注意:如果要切换Runloop的模式,可以采用CFRunLoopStop()主动退出,只有runloop能退出,我们才又机会切换mode。

3. kCFRunLoopCommonModes

首先来一道IOS经典的面试:

主线程默认运行在KCFRunloopDefaultMode下,此时我们在主线程的RunLoop中添加一个timer,模式是KCFRunloopDefaultMode。当我们滑动tableView的时候,定时器就停止了,是为什么呢?

答案很简答:因为主线程的Runloop默认在KCFRunloopDefaultMode模式下,而Runloop每次循环都只会处理当前mode下的timer/source/observer,当我们滑动tableView的时候,Runloop的模式被切换为了UITrackingRunLoopMode,该模式下并没有添加当前的timer,而添加在Default模式下的timer不会运行。我们只需要在添加timer的时候把timer同时添加到UITrackingRunLoopMode下或者添加到kCFRunLoopCommonModes下即可。


六、Timer的添加流程分析

首先Timer的结构如下:

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 */
};
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;

显然NSTimer是对__CFRunLoopTimer的一层封装。

- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;

而performSelector的延时API,底层也是基于Timer实现的。
所以当我们创建Timer,或者用performSelector延时函数的时候,最终都会添加到Runloop的mode下,才能运行。

用两个方法:(1)将timer分别添加到对应的mode (2)将timer添加到commonModes

- (void)func1 {
    self.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {}];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:UITrackingRunLoopMode];
}

- (void)func2 {
    self.timer = [NSTimer timerWithTimeInterval:1 repeats:true block:^(NSTimer * _Nonnull timer) {}];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"%@",[NSRunLoop currentRunLoop]);
}

通过打印先得出结论:

(1)当分别添加到对应的mode下时,timer会添加到每一个mode的timers数组中。

(2)当添加到commonModes中时,timer不仅会添加到每一个mode的timers数组中,并且会添加到common items中。

下面主要分析CFRunLoopAddTimer函数(去掉了非核心代码):

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
    //......
    //判断如果runloop在释放,直接返回
    if (__CFRunLoopIsDeallocating(rl)) return;
      //.......
    //判断当前传入的是否是kCFRunLoopCommonModes
    if (modeName == kCFRunLoopCommonModes) {
        //取出_commonModes中的mode
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        //判断commonModeItems是否为空,如果为空就创建一个集合
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
        //将timer添加到commonModeItems中
        CFSetAddValue(rl->_commonModeItems, rlt);
        if (NULL != set) {
            //2个参数:1 runloop 2 timer
            CFTypeRef context[2] = {rl, rlt};
            /* add new item to all common-modes */
            //set集合中每一个mode都调用__CFRunLoopAddItemToCommonModes方法
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
            CFRelease(set);
        }
    } else {
        //通过__CFRunLoopFindMode获取mode
        CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
        if (NULL != rlm) {
            //判断timers并创建timers
            if (NULL == rlm->_timers) {
                CFArrayCallBacks cb = kCFTypeArrayCallBacks;
                cb.equal = NULL;
                rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
            }
        }
        //当mode有,但是定时器中modes又没有mode时
        if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
            .....
            if (NULL == rlt->_runLoop) {
                rlt->_runLoop = rl;
            } else if (rl != rlt->_runLoop) {
               //定时器已关联的runloop与当前runloop不一致,返回
                  //......
                return;
            }
            //定时器的modes 添加该mode的名称
            CFSetAddValue(rlt->_rlModes, rlm->_name);
              //......
            //采用mktimer(mach kernel timer)通过machport 和 machmsg 触发定时器事件 或者 gcd的timer
            __CFRepositionTimerInMode(rlm, rlt, false);
               //........    
        }
           //......
    }
       //........
}
//将一个item添加到被commonModes标记的所有mode下
static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
    //模式
    CFStringRef modeName = (CFStringRef)value;
    //runloop
    CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
    //item
    CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
    //判断类型并添加到指定的mode下
    if (CFGetTypeID(item) == __kCFRunLoopSourceTypeID) {
        CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
    } else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) {
        CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
    } else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) {
        CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
    }
}

以上就是添加timer的核心源码。
结论:
(1)当添加timer指定模式为非NSRunLoopCommonModes时,timer会被添加到指定mode下。
(2)当添加timer指定模式为NSRunLoopCommonModes时,timer会被添加到_commonModeItems中,并将timer追加到_commonModes中已经标记的模式下。
移除timer的函数就不分析了,基本原理差不多。
继续分析timer添加到_commonModeItems中用来做什么:

//将mode添加到commonModes中
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) {
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return;
    __CFRunLoopLock(rl);
    if (!CFSetContainsValue(rl->_commonModes, modeName)) {
        //获取_commonModeItems集合
        CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL;
        //将mode添加到_commonModes
        CFSetAddValue(rl->_commonModes, modeName);
        if (NULL != set) {
            CFTypeRef context[2] = {rl, modeName};
            /* add all common-modes items to new mode */
            //将commonItems中的item都添加到新的mode下
            CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context);
            CFRelease(set);
        }
    } else {
    }
    __CFRunLoopUnlock(rl);
}
//将commonModeItems中的所有item添加到指定model下
static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) {
    //item
    CFTypeRef item = (CFTypeRef)value;
    //runloop
    CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
    //mode
    CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]);
    
    if (CFGetTypeID(item) == __kCFRunLoopSourceTypeID) {
        CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
    } else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) {
        CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
    } else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) {
        CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
    }
}

例如:当我们指定一个timer为commonMode时,这个timer应该被追加到所有的mode下。如果一开始commonModes中就一个mode,timer也就是只能在这一个mode下运行。当我们再添加一个mode到commonModes中时,timer也需要被追加到新的mode下,此时commonItems就会发挥它的作用了,也就是当
调用CFRunLoopAddCommonMode时,会将commonItems中的所有item都追加到新的被commoModes标记的mode下。


七、Runloop的工作流程

经过以上分析,已经对Runloop的内部结构有了初步的认识,接下来就是Runloop的核心流程:

1、主流程

首先Runloop的入口函数:CFRunLoopRun 和 CFRunLoopRunInMode

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);
}

其中核心的函数是CFRunLoopRunSpecific(忽略非核心代码):

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    //....
    //根据modeName找到本次运行的mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    //如果没找到 || mode中没有注册任何事件,则就此停止,不进入循环
    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);
    //取上一次运行的mode
    CFRunLoopModeRef previousMode = rl->_currentMode;
    //如果本次mode和上次的mode一致
    rl->_currentMode = currentMode;
    //初始化一个result为kCFRunLoopRunFinished
    int32_t result = kCFRunLoopRunFinished;
    if (currentMode->_observerMask & kCFRunLoopEntry )
        /// 1. 通知 Observers: RunLoop 即将进入 loop。
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    if (currentMode->_observerMask & kCFRunLoopExit )
        /// 10. 通知 Observers: RunLoop 即将退出。
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
   //.....
    return result;
}

当Runloop进入循环后的核心函数为__CFRunLoopRun(忽略非核心代码):

补充: 由于最近看了swift开源的CoreFoundation库,对于USE_DISPATCH_SOURCE_FOR_TIMERS这个编译条件,更新如下:(ios系统下的NSTimer也使用了gcd的timer)

#if DEPLOYMENT_RUNTIME_OBJC
#define USE_DISPATCH_SOURCE_FOR_TIMERS __HAS_DISPATCH__
#else
#define USE_DISPATCH_SOURCE_FOR_TIMERS 0
#endif
/**
 *  运行run loop
 *
 *  @param rl              运行的RunLoop对象
 *  @param rlm             运行的mode
 *  @param seconds         run loop超时时间
 *  @param stopAfterHandle true:run loop处理完事件就退出  false:一直运行直到超时或者被手动终止
 *  @param previousMode    上一次运行的mode
 *
 *  @return 返回4种状态
 */

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    //获取系统启动后的CPU运行时间,用于控制超时时间
    uint64_t startTSR = mach_absolute_time();
    
    // 判断当前runloop的状态是否关闭
    if (__CFRunLoopIsStopped(rl)) {
        __CFRunLoopUnsetStopped(rl);
        return kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
        rlm->_stopped = false;
        return kCFRunLoopRunStopped;
    }
    //mach端口,在内核中,消息在端口之间传递。 初始为0
    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)));
    //如果在主线程 && runloop是主线程的runloop && 该mode是commonMode,则给mach端口赋值为主线程收发消息的端口
    if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();
    mach_port_name_t modeQueuePort = MACH_PORT_NULL;
    if (rlm->_queue) {
        //mode赋值为dispatch端口_dispatch_runloop_root_queue_perform_4CF
        modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
        if (!modeQueuePort) {
            CRASH("Unable to get port for run loop mode queue (%d)", -1);
        }
    }
    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) {
        //seconds为超时时间,超时时执行__CFRunLoopTimeout函数
        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;
    }
    
    //标志位默认为true
    Boolean didDispatchPortLastTime = true;
    //记录最后runloop状态,用于return
    int32_t retVal = 0;
    //开始进入do while循环
    do {
        //初始化一个存放内核消息的缓冲池
        uint8_t msg_buffer[3 * 1024];
        mach_msg_header_t *msg = NULL;
        mach_port_t livePort = MACH_PORT_NULL;
        //取所有需要监听的port
        __CFPortSet waitSet = rlm->_portSet;
        
        //设置RunLoop为可以被唤醒状态
        __CFRunLoopUnsetIgnoreWakeUps(rl);
        
        /// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        if (rlm->_observerMask & kCFRunLoopBeforeSources)
            /// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        
        /// 执行被加入的block
        __CFRunLoopDoBlocks(rl, rlm);
        /// 4. RunLoop 触发 Source0 (非port) 回调。
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            /// 执行被加入的block
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        //如果没有Sources0事件处理 并且 没有超时,poll为false
        //如果有Sources0事件处理 或者 超时,poll都为true
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
        //第一次do..whil循环不会走该分支,因为didDispatchPortLastTime初始化是true
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
            //从缓冲区读取消息
            msg = (mach_msg_header_t *)msg_buffer;
            /// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
                //如果接收到了消息的话,前往第9步开始处理msg
                goto handle_msg;
            }
        }
        didDispatchPortLastTime = false;
        /// 6.通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        //设置RunLoop为休眠状态
        __CFRunLoopSetSleeping(rl);
        __CFPortSetInsert(dispatchPort, waitSet);
        
        __CFRunLoopModeUnlock(rlm);
        __CFRunLoopUnlock(rl);
        
        //这里有个内循环,用于接收等待端口的消息
        //进入此循环后,线程进入休眠,直到收到新消息才跳出该循环,继续执行run loop
        do {
            if (kCFUseCollectableAllocator) {
                objc_clear_stack(0);
                memset(msg_buffer, 0, sizeof(msg_buffer));
            }
            
            msg = (mach_msg_header_t *)msg_buffer;
            //7.接收waitSet端口的消息
            /// • 一个基于 port 的Source 的事件。
            /// • 一个 Timer 到时间了
            /// • RunLoop 自身的超时时间到了
            /// • 被其他什么调用者手动唤醒
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
            //收到消息之后,livePort的值为msg->msgh_local_port,
            if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
                // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
                while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
                if (rlm->_timerFired) {
                    // Leave livePort as the queue port, and service timers below
                    rlm->_timerFired = false;
                    break;
                } else {
                    if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
                }
            } else {
                // Go ahead and leave the inner loop.
                break;
            }
        } while (1);
        
        __CFRunLoopLock(rl);
        __CFRunLoopModeLock(rlm);
        
        __CFPortSetRemove(dispatchPort, waitSet);
        
        __CFRunLoopSetIgnoreWakeUps(rl);
        
        // user callouts now OK again
        //取消runloop的休眠状态
        __CFRunLoopUnsetSleeping(rl);
        /// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
        /// 收到消息,处理消息。
    handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);

        if (MACH_PORT_NULL == livePort) {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
        } else if (livePort == rl->_wakeUpPort) {
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
        }
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            //dispatch_timer
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            /// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer, because we apparently fired early
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            //mk_timer
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        /// 9.2 如果有dispatch到main_queue的block,执行block
        else if (livePort == dispatchPort) {
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;
            didDispatchPortLastTime = true;
        } else {
            /// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            // Despite the name, this works for windows handles as well
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
                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);
                }
            }
        }
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
        
        /// 执行加入到Loop的block
        __CFRunLoopDoBlocks(rl, rlm);
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            /// 进入loop时参数处理完事件就返回。
            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)) {
            /// source/timer/observer一个都没有了
            retVal = kCFRunLoopRunFinished;
        }
        /// 如果没超时,mode里没空,loop也没被停止,那继续loop。
    } while (0 == retVal);
    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }   
    return retVal;
}

上面的核心代码非常的长,总结大致流程如下:。
(1)判断RunLoop的状态是否关闭。
(2)判断runloop是否超时(利用gcd的timer)。
(3)初始化一个存放内核消息的缓冲池,取所有需要监听的port,设置RunLoop为可以被唤醒状态。
(4)通知 Observers: RunLoop 即将触发 Timer 回调。
(5)通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
(6) RunLoop 触发 Source0 (非port) 回调。
(7)如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。
(8)通知 Observers: RunLoop 的线程即将进入休眠(sleep)。设置RunLoop为休眠状态,之后就是用户态切换到内核态
(9)接收waitSet端口的消息
(10)取消runloop的休眠状态,通知 Observers: RunLoop 的线程刚刚被唤醒了。内核态切换到用户态不久
(11)收到消息,处理消息。(1、如果一个 Timer 到时间了,触发这个Timer的回调。2、如果有dispatch到main_queue的block,执行block 3、如果一个 Source1 (基于port) 发出事件了,处理这个事件)
(12)判断设置runloop的状态

流程图如下:

补充:

1.用户态:应用程序都是在用户态,平时开发用到的api等都是用户态的操作
2.内核态:系统调用,牵涉到操作系统,底层内核相关的指令。
当Runloop进入休眠的时候,其实就是用户态向内核态的转换,并不是一个white死循环,而是状态切换。
当Runloop被唤醒之后,就是内核态向用户态转换。

内核态和用户态的转换,实质是调用了mach_msg()函数。

2、RunLoop的状态总结

typedef CF_ENUM(SInt32, CFRunLoopRunResult) {
    kCFRunLoopRunFinished = 1,
    kCFRunLoopRunStopped = 2,
    kCFRunLoopRunTimedOut = 3,
    kCFRunLoopRunHandledSource = 4
};

(1)kCFRunLoopRunFinished 完成状态,当没有timer/source的时候
(2)kCFRunLoopRunStopped 停止状态,手动停止的时候
(3)kCFRunLoopRunTimedOut 超时状态,当Runloop运行超时的时候
(4)kCFRunLoopRunHandledSource 处理完事件就返回

3、RunLoop的活动状态总结

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0), //进入RunLoop
    kCFRunLoopBeforeTimers = (1UL << 1), //RunLoop即将触发定时器事件
    kCFRunLoopBeforeSources = (1UL << 2), //RunLoop即将处理Source事件
    kCFRunLoopBeforeWaiting = (1UL << 5), //RunLoop即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6), //RunLoop即将被唤醒,但尚未开始处理唤醒它的事件
    kCFRunLoopExit = (1UL << 7),   //退出RunLoop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

4、RunLoop的响应总结

(1)调用Timer (CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION)

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info) {
    if (func) {
        func(timer, info);
    }
    getpid(); // thwart tail-call optimization
}

每当Timer的时间到了之后,会触发__CFRunLoopDoTimers -> __CFRunLoopDoTimer ->__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__的回调函数,通过保存在timer中的CFRunLoopTimerCallBack回调出来。

(2)GCD主队列(CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE

static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() __attribute__((noinline));
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg) {
    _dispatch_main_queue_callback_4CF(msg);
    getpid(); // thwart tail-call optimization
}

通过上面Runloop主流程分析,在Runloop唤醒之后,会判断是否存在GCD主队列的block,如果存在就会执行 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
当我使用主队列异步函数的时候,就会触发此回调。

(3)block应用 (CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK)

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void)) {
    if (block) {
        block();
    }
    getpid(); // thwart tail-call optimization
}

当上层调用:

[[NSRunLoop currentRunLoop] performBlock:^{
        NSLog(@"1");
 }];

的时候会触发__CFRunLoopDoBlocks->CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK

(4)响应Source0 (CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION)

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info) {
    if (perform) {
        perform(info);
    }
    getpid(); // thwart tail-call optimization
}

Runloop每一次循环都会去调用__CFRunLoopDoSources0->CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION

(5)响应Source1 (CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION)

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
                                                                       void *(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info),
                                                                       mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply,
#else
                                                                       void (*perform)(void *),
#endif
                                                                       void *info) {
    if (perform) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        *reply = perform(msg, size, kCFAllocatorSystemDefault, info);
#else
        perform(info);
#endif
    }
    getpid(); // thwart tail-call optimization
}

当Runloop收到内核事件之后,会调用__CFRunLoopDoSource1->CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION

(6)Observer源(CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION

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
}

当Runloop处于上面总结的活动状态时,会调用__CFRunLoopDoObservers->CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION

八、Source0和Source1

Source的结构如下:

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;
};

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, CFRunLoopMode mode);
    void	(*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode 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_OSX || 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;

首先__CFRunLoopSource结构体里面,有一个联合体(为了节省内存开销)。联合体中也就是Source0和Source1。

1、Source0

先来看Source0,当我们在上层调用:

///子线程/主线程->主线程
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
///子线程/主线程->子线程,	
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array 
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait 
- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg 

这些函数的时候,就会将事件添加到Source0中(子线程/主线程->子线程的方法必须保证执行事件的子线程Runlopp开启)。
模拟Souce0实现:

......
@property (nonatomic) CFRunLoopSourceRef source0;
@property (nonatomic) CFRunLoopRef runloop;
......
#pragma mark - TestSource0
- (void)performSelectorSource0 {
    __weak typeof(self)weakSelf = self;
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        void *info = (__bridge void *)weakSelf;
        CFRunLoopSourceContext context = {0,info,NULL,NULL,NULL,NULL,NULL,&schedule,&cancel,&perform};
        weakSelf.source0 = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
        CFRunLoopAddSource(CFRunLoopGetCurrent(),weakSelf.source0, kCFRunLoopDefaultMode);
        weakSelf.runloop = CFRunLoopGetCurrent();
        [[NSRunLoop currentRunLoop] run];
    }];
    thread.name = @"xxx";
    [thread start];
    
}

///与Source0相关的回调
void schedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode) {
    NSLog(@"source0已加入子线程的runloop中");
}
void cancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode) {
    NSLog(@"source0已从子线程的runloop中移除");

}
void perform(void *info) {
    NSLog(@"获取主线程的消息");
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //触发Source0
    CFRunLoopSourceSignal(_source0);
    //唤醒Runloop
    CFRunLoopWakeUp(_runloop);
}

- (void)dealloc {
    CFRunLoopRemoveSource(_runloop, _source0, kCFRunLoopDefaultMode);
    CFRunLoopStop(_runloop);
}

Souce0相当于被包装的带有上下文的函数,需要主动触发,这个函数才会被执行。

2、Source1

Source1是基于mach_port的事件,它是内核事件,苹果系统的内核是XNU混合内核,包括了Mach内核和BSD内核。BSD主要提供在Mach之上标准化的API,Mach是核心。Mach负责线程与进程管理、虚拟内存管理、进程之间通信和消息传递、任务调度等。
基于Source1的事件传递,主要基于内核接口:

///System Trap / Function — Sends and receives a message using the same mes- sage buffer
mach_msg_return_t mach_msg(mach_msg_header_t *msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_port_name_t notify

这个方法底层会基于硬件异常:陷阱(trap)实现事件传递。陷阱最终的用途,是在用户程序和内核之间提供接口,称为系统调用。

下面来模拟一下Source1事件

使用CFMessagePortRef来模拟Source1事件。

.....
@property (nonatomic) CFMessagePortRef subPort;
@property (nonatomic) CFMessagePortRef mainPort;
@property (nonatomic, strong) NSThread *subThread;
@end

.......

- (void)viewDidLoad {
    [super viewDidLoad];
 
    //创建主线程的port
    [self createMainPort];
    
    //创建子线程
    __weak typeof(self)weakself = self;
    [NSThread detachNewThreadWithBlock:^{
        weakself.subThread = [NSThread currentThread];
        [weakself createSubPort];
        CFRunLoopRun();
    }];
}

//创建主线程端口
- (void)createMainPort {
    void *mainInfo = (__bridge void*)self;
    CFMessagePortContext mainPortContext = {0,mainInfo,NULL,NULL,NULL};
    Boolean shouldFreeInfo;
    //创建mach_port
    CFMessagePortRef mach_port = CFMessagePortCreateLocal(kCFAllocatorDefault,  CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("com.test.main_mach_port")), &_messagePortCallBack, &mainPortContext, &shouldFreeInfo);
    self.mainPort = mach_port;
    if (self.mainPort != NULL) {
        CFRunLoopSourceRef source1 = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, self.mainPort, 0);
        if (source1 != NULL) {
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source1, kCFRunLoopDefaultMode);
            CFRelease(source1);
            CFRelease(mach_port);
        }
    }
}

//创建子线程端口
- (void)createSubPort {
    void *info = (__bridge void*)self;
    CFMessagePortContext portContext = {0,info,NULL,NULL,NULL};
    Boolean shouldFreeInfo;
    //创建mach_port
    CFMessagePortRef mach_port = CFMessagePortCreateLocal(kCFAllocatorDefault,  CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("com.test.sub_mach_port")), &_messagePortCallBack, &portContext, &shouldFreeInfo);
    ///保存端口,建立双向信道
    self.subPort = mach_port;
    if (self.subPort != NULL) {
        CFRunLoopSourceRef source1 = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, self.subPort, 0);
        if (source1 != NULL) {
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source1, kCFRunLoopDefaultMode);
            CFRelease(source1);
            CFRelease(mach_port);
        }
    }
}

//Source1的回调函数
CFDataRef _messagePortCallBack(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) {
    ///桥接info获取oc对象...
    const UInt8 *buffer = CFDataGetBytePtr(data);
    CFIndex index = CFDataGetLength(data);
    CFStringRef messageref = CFStringCreateWithBytes(kCFAllocatorDefault, buffer, index, kCFStringEncodingUTF8, false);
    NSString *message = (__bridge_transfer NSString*)messageref;
    
    NSLog(@"%@:消息ID:%d 收到数据:%@", [NSThread currentThread],msgid,message);
    return NULL;
}

//发送消息 主线程->主线程
- (void)mainToMain {
    CFStringRef message = CFSTR("你好,我来自主线程");
    CFDataRef outData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
    ///发送消息, 10001 代表 主线程->主线程
    CFMessagePortSendRequest(_mainPort, 1001, outData, 0.1, 0.0, NULL, NULL);
    ///释放资源
    CFRelease(outData);
    CFRelease(message);
}

//发送消息 主线程->子线程
- (void)mainToThread {
    CFStringRef message = CFSTR("你好,我来自主线程");
    CFDataRef outData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
    ///发送消息, 10001 代表 主线程->子线程
    CFMessagePortSendRequest(_subPort, 1002, outData, 0.1, 0.0, NULL, NULL);
    ///释放资源
    CFRelease(outData);
    CFRelease(message);
}

//发送消息 子线程->主线程
- (void)threadToMain {
    [self performSelector:@selector(sendMsgToMainThread) onThread:self.subThread withObject:nil waitUntilDone:NO];
}

- (void)sendMsgToMainThread {
    NSData *data = [@"你好,我来自子线程" dataUsingEncoding:NSUTF8StringEncoding];
    CFDataRef msgData = (__bridge_retained CFDataRef)data;
    CFMessagePortSendRequest(_mainPort, 1003, msgData, 0.1, 0.0, NULL, NULL);
    CFRelease(msgData);
}

//发送消息 子线程->子线程
- (void)threadToThread {
    [self performSelector:@selector(sendMsgToSubThread) onThread:self.subThread withObject:nil waitUntilDone:NO];
}

- (void)sendMsgToSubThread {
    CFStringRef message = CFSTR("你好,我来自子线程");
    CFDataRef outData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
    CFMessagePortSendRequest(_subPort, 1004, outData, 0.1, 0.0, NULL, NULL);
    CFRelease(outData);
    CFRelease(message);
}

以上就是基于CFMessagePortRef的demo。

当我们有子线程常驻需求的时候,我们就可以给子线程的RunLoop添加一个NSPort,然后run。Runloop就可以一直运行,线程就可以常驻了。

总结:

1、Source0就是一个包装了上下文信息的回调函数,需要主动通过CFRunLoopSourceSignal触发,然后通过CFRunLoopWakeUp唤醒Runloop去处理。它的主要作用是处理app内部事件以及app自己负责管理的事务。

2、Source1是基于mach_port实现的回调函数,可以实现多端通信。


九、 Observer

通过给Runloop添加Observer,可以来监听Runloop的事件状态。
Observer的结构如下:

struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    _CFRecursiveMutex _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount; //被添加到Rl几次
    CFOptionFlags _activities;		//需要观察RL哪些状态
    CFIndex _order;			//状态事件触发时,依次通知的观察者,值越小优先级越高
    CFRunLoopObserverCallBack _callout;//状态事件触发时的回调
    CFRunLoopObserverContext _context;	// 上下文	
};

一个观察者,可以被添加到RunLoop的多个Mode下,同一个Mode下也可以有多个Oberserver,当事件触发时,观察者是依据_order值从小到大的顺序进行事件回调。

比如NSOrderedPerform的分类也是基于Observer的。

@interface NSRunLoop (NSOrderedPerform)

- (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes;
- (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg;
- (void)cancelPerformSelectorsWithTarget:(id)target;

@end

自定义监听demo如下:


- (void)addRunloopObserver {
    void *info = (__bridge void *)self;
    CFRunLoopObserverContext context = {0,info,NULL,NULL,NULL};
    _changeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, true, 0, _runLoopObserverCallBack, &context);
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), _changeObserver, kCFRunLoopCommonModes);
}

///回调函数
void _runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    ///获取mode名称
    NSString* mode = (__bridge NSString*)CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
    if (info) {//桥接info为oc对象
        NSLog(@"%@",info);
        Test13ViewController *vc = (__bridge Test13ViewController*)info;
        vc.view.backgroundColor = [UIColor whiteColor];
    }
    switch (activity) {
        //do somthing...
        case kCFRunLoopEntry:
            NSLog(@"进入");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"即将处理timer");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"即将处理Source0");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"即将休眠");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"刚刚唤醒");
            break;
        case kCFRunLoopExit:
            NSLog(@"退出");
            break;
    }
}

- (void)dealloc {
    CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), _changeObserver, kCFRunLoopCommonModes);
    CFRelease(_changeObserver);
    NSLog(@"%s",__func__);
}