RunLoop是什么?

RunLoopiOS/Mac OS开发中比较重要的知识点,它贯穿程序工作的整个进程。它是线程基础架构的一部分,是一种保证线程循环处理工作而不会退出的机制。一同也担任办理线程需求处理的工作,让线程有事儿时繁忙,没事儿时休眠。

每个线程都有一个相关的RunLoop方针,子线程的RunLoop是需求手动打开的,主线程的RunLoop作为运用发起的一部分由系统主动打开。

iOS/Mac OS供应了NSRunLoopCFRunLoopRef两个方针,帮忙我们配置和办理线程的RunLoopCFRunLoopRef供应纯C完结并且线程安全的API;NSRunLoop是依据CFRunLoopRef封装的面向方针的API,这个API不是线程安全的。

RunLoop与线程的联络

苹果是不主张我们自己创建RunLoop方针,但是我们可以通过下列方法获取特定线程下的RunLoop方针:

[NSRunLoop currentRunLoop];
[NSRunLoop mainRunLoop];
//CoreFoundation
CFRunLoopGetMain();
CFRunLoopGetCurrent();

CoreFoundation是开源的(下载地址),我们可以查看CFRunLoopRef的关于CFRunLoopGetMainCFRunLoopGetCurrent的完结:

CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    // pthread_main_thread_np() 获取主线程
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    // pthread_self() 获取其时线程
    return _CFRunLoopGet0(pthread_self());
}
///全局`Dictionary`
static CFMutableDictionaryRef __CFRunLoops = NULL;
///访问`Dictionary`需求的锁
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);
    /// `Dictionary`不存在
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
        /// 创建局部变量`Dictionary`
	CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        /// 【创建主线程的`RunLoop`方针】
	CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        /// 主线程方针的实践地址,以该地址为`Key`,以`mainLoop`为`Value`存入局部变量`Dictionary`中
	CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        ///将局部变量`dict`的值 写入全局字典`__CFRunLoops`对应的地址中
	if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
	    CFRelease(dict);
	}
	CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    /// 从全局字典`__CFRunLoops`获取线程对应的`RunLoop`
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    ///假设线程对应的`RunLoop`不存在
    if (!loop) {
	CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
	loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
	if (!loop) {
        /// 创建`RunLoop`,取线程方针的实践地址,
        /// 以该地址为`Key`,以`newLoop`为`Value`存入全局变量`__CFRunLoops`中
	    CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
	    loop = newLoop;
	}
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFUnlock(&loopsLock);
	CFRelease(newLoop);
    }
    ///是其时线程
    if (pthread_equal(t, pthread_self())) {
        /// 将`RunLoop`方针以`__CFTSDKeyRunLoop`为`key`,储存到线程的本地(私有)数据空间,
        ///析构函数为`NULL`
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        /// `CFInternal.h`定了枚举`__CFTSDKeyRunLoop` = 10 与 `__CFTSDKeyRunLoopCntr` = 11 
        /// 假设线程TSD,枚举`__CFTSDKeyRunLoopCntr` 对应`slot`未存值
        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
        /// 为其存值`PTHREAD_DESTRUCTOR_ITERATIONS-1`,并设置析构函数`__CFFinalizeRunLoop`,
        ///此举目的是为了:当线程销毁时,完结对`RunLoop`的销毁
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
       /// 怎么完结的呢?请继续往下看一探毕竟!
        }
    }
    return loop;
}
///上述代码的`_CFGetTSD`与`_CFSetTSD`的完结如下:
///TSD: Thread Specific Data
typedef struct __CFTSDTable {
    uint32_t destructorCount;
    ///`uintptr_t` 存储指针的,无符号整数类型,
    /// 数组个数为`CF_TSD_MAX_SLOTS`
    uintptr_t data[CF_TSD_MAX_SLOTS];
    tsdDestructor destructors[CF_TSD_MAX_SLOTS];
} __CFTSDTable;
// For the use of CF and Foundation only
CF_EXPORT void *_CFGetTSD(uint32_t slot) {
    // Get or initialize a thread local storage,It is created on demand
    __CFTSDTable *table = __CFTSDGetTable();
    //...
    uintptr_t *slots = (uintptr_t *)(table->data);
    return (void *)slots[slot];
}
// For the use of CF and Foundation only
CF_EXPORT void *_CFSetTSD(uint32_t slot, void *newVal, tsdDestructor destructor) {
    /// Get or initialize a thread local storage,It is created on demand
    __CFTSDTable *table = __CFTSDGetTable();
    ///...
    void *oldVal = (void *)table->data[slot];
    ///...
    table->data[slot] = (uintptr_t)newVal;
    ///析构函数相关
    table->destructors[slot] = destructor;
    return oldVal;
}
// Get or initialize a thread local storage. It is created on demand.
static __CFTSDTable *__CFTSDGetTable() {
   /// 通过`CF_TSD_KEY`获取线程对应数据
    __CFTSDTable *table = (__CFTSDTable *)__CFTSDGetSpecific();
    // Make sure we're not setting data again after destruction.
    if (table == CF_TSD_BAD_PTR) {
        return NULL;
    }
    // Create table on demand
    if (!table) {
        // This memory is freed in the finalize function
        table = (__CFTSDTable *)calloc(1, sizeof(__CFTSDTable));
        // Windows and Linux have created the table already, we need to initialize it here for other platforms. On Windows, the cleanup function is called by DllMain when a thread exits. On Linux the destructor is set at init time.
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        ///初始化一个键`CF_TSD_KEY`,并相关析构函数
        /// `CF_TSD_KEY` = 55 ,在不同线程该`Key`值可以同享,但此`Key`对应的值却是不同的。
        ///每个线程都会有自己的`tsd`,它们共用`CF_TSD_KEY`这个`key`
        /// 线程销毁时苹果系统会调用:
        /// `_pthread_exit` -> `_pthread_tsd_cleanup` -> 
        ///`_pthread_tsd_cleanup_new`->`_pthread_tsd_cleanup_key`
        ///当线程销毁时,会调用相关的析构函数`__CFTSDFinalize`,保证线程对应的私有数据也能销毁 
        ///具体可参照函数: _pthread_tsd_cleanup_key
        /// 函数完结[细节](https://github.com/apple/darwin-libpthread/blob/main/src/pthread_tsd.c)
        pthread_key_init_np(CF_TSD_KEY, __CFTSDFinalize);
#endif
        // 为`CF_TSD_KEY`指定需求存储的数据
        __CFTSDSetSpecific(table);
    } 
    return table;
}
///销毁线程对应的TSD
static void __CFTSDFinalize(void *arg) {
    ///...
    __CFTSDTable *table = (__CFTSDTable *)arg;
    ///遍历全部插槽 比方存`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;
           //遍历到i= 11 =`__CFTSDKeyRunLoopCntr`时,调用`__CFFinalizeRunLoop`,释放`RunLoop`
            table->destructors[i]((void *)(old));
        }
    }
    if (table->destructorCount == PTHREAD_DESTRUCTOR_ITERATIONS - 1) {    // On PTHREAD_DESTRUCTOR_ITERATIONS-1 call, destroy our data
        free(table);
        ///...
        __CFTSDSetSpecific(CF_TSD_BAD_PTR);
        return;
    }
}

_CFGetTSD_CFSetTSD源码查看可前往此处。

总结:

  1. RunLoop与线程之间是一一对应的
  2. 当线程需求获取对应的RunLoop时,才会创建RunLoop方针
  3. 线程销毁的时分会销毁RunLoop方针

RunLoop的相关类

CoreFoundation中与RunLoop有关的5个结构体:

CFRunLoopRef //runLoop方针
CFRunLoopModeRef //runLoop工作的方式
CFRunLoopTimerRef// 依据时间的触发器
CFRunLoopSourceRef//工作源,source0:自定义工作输入源 和 source1 :依据mach内核端口的工作源
CFRunLoopObserverRef //用于监听runLoop工作情况的查询者

它们之间的联络如下:

谈谈 iOS RunLoop 底层

具体可通过打印[NSRunLoop currentRunLoop]查看,也可通过查看CFRunLoopRefCFRunLoopModeRef的结构定义。两者的结构定义如下:

///CFRunLoop.c
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
    //...
    CFStringRef _name;
    //...
    CFMutableSetRef _sources0; // <Set>
    CFMutableSetRef _sources1;// <Set>
    CFMutableArrayRef _observers; // <Array>
    CFMutableArrayRef _timers; // <Array>
    //...
};
///CFRunLoop.h 类型重命名
typedef struct __CFRunLoop * CFRunLoopRef;
///CFRunLoop.c 结构体
struct __CFRunLoop {
    //..
    CFMutableSetRef _commonModes; // <Set> String UITrackingRunLoopMode/kCFRunLoopDefaultMode
    CFMutableSetRef _commonModeItems;// <Set> observer/source/timer
    CFRunLoopModeRef _currentMode; //其时工作的mode
    CFMutableSetRef _modes; //内置的modes;
    //...
};

RunLoop的方式

每次工作RunLoop都需求指定一个Mode,该Mode会被设置为_currentMode,只要与该Mode相关的输入源source0source1才干被处理,相同的,监听RunLoop的工作,只要与该Mode相关的observers才干收到告知。

///`RunLoop`指定`Mode`工作
CFRunLoopRunResult CFRunLoopRunInMode(CFRunLoopMode mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);

程序在工作的进程中,会处理依据时间的、系统的、用户的工作,这些工作在程序工作期间有着不同的优先级,为了满意运用层依据优先级对这些工作的办理,系统选用RunLoopMode对这些工作分组,然后交由RunLoop去办理。除了系统定义的默许方式和常用方式,我们也可以自定义方式,但是自定义的方式中必须有相关的工作,否则自定义方式没有任何意义。

_commonModeItems_commonModeskCFRunLoopCommonModes (NSRunLoopCommonModes)背后的完结逻辑,可以了解为选用RunLoopMode对工作进行分组后,我们又希望一些工作可以一同被多个Mode处理,所以我们将这些工作(sources/timers/observers)放入_commonModeItems,将需求一同处理这些工作的多个Mode放入_commonModes调集进行符号;当工作指定kCFRunLoopCommonModes方式进行添加时,先会添加到_commonModeItems中,然后将_commonModeItems中的全部工作追加到_commonModes中现已符号的方式下。

///添加一个`Mode`到`RunLoop`的`commonMode`调集中,一旦添加无法移除。只加不减
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFRunLoopMode mode);

示例:主线程默许工作在kCFRunLoopDefaultMode下,当我们滑动ScrollView时会切换到UITrackingRunLoopMode,而主线程的RunLoop_commonModes默许包含这两种方式; 开发中会遇到在主线程发起一个定时器时,会受视图滑动的影响的问题,解决方法:

[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

将定时器添加到NSRunLoop方针的NSRunLoopCommonModes方式下,毕竟定时器会被添加到kCFRunLoopDefaultModeUITrackingRunLoopMode下;当然也可以自行添加到这两个方式中。

查看CoreFoundationCFRunLoopAddTimer方法的完结,可以更深化的了解_commonModeItems_commonModes的工作原理:

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
    ///...
    if (modeName == kCFRunLoopCommonModes) { ///是否是`kCFRunLoopCommonModes`
        ///取`Runloop`的`_commonModes`
	CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
	if (NULL == rl->_commonModeItems) {
            ///创建`_commonModeItems`<Set>
	    rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
	}
        ///添加定时器到`_commonModeItems`
	CFSetAddValue(rl->_commonModeItems, rlt);
	if (NULL != set) { //`_commonModes`有值
	    CFTypeRef context[2] = {rl, rlt};
	    /* add new item to all common-modes */
            ///为Set调集中的每个元素都调用该方法
	    CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
	    CFRelease(set);
	}
    } else {
        ///非`kCFRunLoopCommonModes`,先找找`runloop`的modes是否有,找不到创建
	CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
	if (NULL != rlm) {
            if (NULL == rlm->_timers) { ///创建寄存`timer`的数组
                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 触发定时器工作
            __CFRepositionTimerInMode(rlm, rlt, false);
            ///...
	}
        ///...
    }
    ///..
}
static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
    CFStringRef modeName = (CFStringRef)value;
    CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
    CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
    if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
	CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);//add source
    } else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
	CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName); // add observer
    } else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
	CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName); // add timer
    }
}

CoreFoundation中向指定RunLoopMode中添加和移除工作的函数有:

//Source
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode);
void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFRunLoopMode mode);
//Timer
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFRunLoopMode mode);
void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFRunLoopMode mode);
//Observer
void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFRunLoopMode mode);
void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFRunLoopMode mode);

RunLoop的工作

当运用程序发起的时分,主线程的RunLoop通过UIApplicationMain函数发起。

谈谈 iOS RunLoop 底层
通进程序发起时,函数的调用栈,发现调用了CFRunLoopRunSpecific; 并且Mode之间的切换通过LLDB调试方法:b CFRunLoopRunSpecificb __CFRunLoopRun,发现也会调用到该方法,源码分析如下:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
    ///进程查看
    CHECK_FOR_FORK();
    ///是否销毁
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    ///互斥锁
    __CFRunLoopLock(rl);
    //从`runloop`的`modes`找到`modeName`对应的`mode`,找不到也不创建
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    ///假设`currentMode`是空的,则回来`kCFRunLoopRunFinished`
    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`存储其时rl的情况
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    CFRunLoopModeRef previousMode = rl->_currentMode;
    rl->_currentMode = currentMode;
    int32_t result = kCFRunLoopRunFinished;
    ///`CurrentMode`的情况为`kCFRunLoopEntry`时,
    ///通过`__CFRunLoopDoObservers`告知其时`Mode`对应的查询者
    /// _observerMask 设置的是rlo需求监听的情况
    ///1.runloop处于`kCFRunLoopEntry`,告知runloopmode->observers,runloop即将进入
	if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
        ///`RunLoop`真实的工作逻辑
	result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
         ///`CurrentMode`的情况为`kCFRunLoopExit`时,
    ///通过`__CFRunLoopDoObservers`告知其时`Mode`对应的查询者
	if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
	rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}
///`RunLoop`工作的中心原理
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    /// TSR: time since repair
    uint64_t startTSR = mach_absolute_time();
    // 判别`RunLoop`或`rlm->_stopped`是否现已间断,间断则实行`return kCFRunLoopRunStopped`。
    ///...
    ///声明 mach_port,当(主线程的消息分发队伍是安全的&其时rl是主线程的rl&rlm->name in  rl-> commonModes),寄存与主线程(主队伍)的runLoop相关的`mach_port`,处理`runloop`内核工作
    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();
    ///  MacOS系统下,设置与Mode相关的队伍对应的端口号(定时器队伍)
#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);
        ///...
    }
#endif
    ///  判别参数`seconds`,决议`RunLoop`的工作时长,当(seconds>0&&seconds<=TIMER_INTERVAL_LIMIT),打开GCD定时器,其他情况 当即超时 和 超时不限
    dispatch_source_t timeout_timer = NULL;
    ///...
	timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
       ///...
	dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
       ///...
        dispatch_resume(timeout_timer);
     Boolean didDispatchPortLastTime = true;
    ///打开do-While死循环 当retVal != 0 时 间断循环
    int32_t retVal = 0;
    do {
///...
        ////声明msg_buffer数组
        uint8_t msg_buffer[3 * 1024];
///...
        ///rlm等候接收来自mach消息的mach_port调集
	__CFPortSet waitSet = rlm->_portSet;
        ///撤销rl疏忽唤醒的设置,使其能接收唤醒消息
        __CFRunLoopUnsetIgnoreWakeUps(rl);
        ///2.runloop处于`kCFRunLoopBeforeTimers`,告知runloopmode->observers,runloop即将触发timer回调
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        ///3.runloop处于`kCFRunLoopBeforeSources`,告知runloopmode->observers,runloop即将触发Source0(非mach_port)回调
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        ///实行runloop通过`struct _block_item *_blocks_head、_blocks_tail`参与runloop的block; 
        ///毕竟调用`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__`
	__CFRunLoopDoBlocks(rl, rlm);
        /// 4. 实行自定义的`source0`工作,毕竟调用`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__`
        // `stopAfterHandle`A flag indicating whether the run loop should exit after processing one source 
        /// 假设rl当即超时或许source0现已处理完毕(rl退出)
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {///source0处理完毕,再次实行被参与的block
            __CFRunLoopDoBlocks(rl, rlm);
	}
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
        ///主线程`runloop`的mach_port有用且不是初次分配
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
        ///...
            msg = (mach_msg_header_t *)msg_buffer;
            ///5.`thread`打开`for(;;)`循环,等候,
            ///通过`mach_msg`等候从rl的`dispatchPort`获取信息,假设成功获取,则跳转处理source1。
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                ///处理msg
                goto handle_msg;
            }
            ///....
        }
        didDispatchPortLastTime = false;
        ///假设rl没有退出 && 处于`kCFRunLoopBeforeWaiting`情况
        ///6.runloop处于`kCFRunLoopBeforeWaiting`,告知runloopmode->observers,runloop即将进入休眠(sleep)
	if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        ///设置runloop的情况为sleep,此处设置1
        ///Bit 0 of the base reserved bits is used for stopped state
        ///Bit 1 of the base reserved bits is used for sleeping state
        ///Bit 2 of the base reserved bits is used for deallocating state
	__CFRunLoopSetSleeping(rl);
        ///参与rlm的waitset中
        __CFPortSetInsert(dispatchPort, waitSet);
        ///...
        ///设置rl休眠初步的时间,rl退出为0 否则为其时肯定时间
        CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
        ///...
        ///7. 通过`__CFRunLoopServiceMachPort`实行`if(TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } 
        ///让线程进入休眠,等候被`mach_msg`函数唤醒
        msg = (mach_msg_header_t *)msg_buffer;
        ///参数超时时间为`TIMEOUT_INFINITY`触发rl的sleep,(rlm的portSet) poll = false 标识rl 未间断,未超时 waitSet 还有定时器port
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
        ///macOS会循环实行rlm->queue中工作,直到all done
    ///...
        ///rl被唤醒,计算rl的休眠时间
        rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
        ///从rlm的waitSet(portSet)中移除
        __CFPortSetRemove(dispatchPort, waitSet);
        ///rl已被唤醒,故设置rl疏忽唤醒消息
        __CFRunLoopSetIgnoreWakeUps(rl);
        // user callouts now OK again
        ///撤销rl的sleeping情况
	__CFRunLoopUnsetSleeping(rl);
        ///假设rl没有退出 && 处于`kCFRunLoopAfterWaiting`情况 
        ///8.runloop处于`kCFRunLoopAfterWaiting`,告知runloopmode->observers,runloop即将被唤醒
	if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        ///收到来自mach_port的消息会跳转此处实行
        handle_msg:;
        ///rl已被唤醒,故设置rl疏忽唤醒消息
        __CFRunLoopSetIgnoreWakeUps(rl);
        ///....
        ///9.被唤醒处理工作
        ///`__CFRunLoopServiceMachPort`调用`mach_msg`成功,会设置`livePort`的值为消息来源的端口
        if (MACH_PORT_NULL == livePort) {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();///// livePort为空,do nothing
            // handle nothing
        } else if (livePort == rl->_wakeUpPort) {/// 通过调用`CFRunLoopWakeUp`函数唤醒rl
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();//
            // do nothing on Mac OS
        }
        /// 9.1 被定时器唤醒,处理定时器工作
        ///被GCD Timer唤醒
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            ///假设未处理,重设下次触发时间
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer, because we apparently fired early
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
        ///被MK Timer唤醒
#if USE_MK_TIMER_TOO
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
       ///9.2 处理dispatch到mainQueue的block工作
        else if (livePort == dispatchPort) {
           /// DISPATCH 唤醒 runloop
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
           ///线程Data以`__CFTSDKeyIsInGCDMainQ`为key 存 6
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
            ///`_dispatch_main_queue_callback_4CF`,处理msg
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            ///线程Data以`__CFTSDKeyIsInGCDMainQ`为key 存 0,用来在函数初步时判别`libdispatchQSafe`的值。
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
            ///..
            sourceHandledThisLoop = true;
            didDispatchPortLastTime = true;
        } else {
            ///9.3 被依据mach_port的source1唤醒,处理此工作
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
             ///...
            /// 从rlm->_portToV1SourceMap的字典中,取出Source1工作
            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;
                ///处理Source1,调用`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__`
		sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
		if (NULL != reply) {///处理完source1,假设需求回复消息,则实行消息回复
		    (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
		    CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
		}
                ///...
#endif
	    }
            ///...
        } 
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif
        ////实行一下参与runloop的blocks
	__CFRunLoopDoBlocks(rl, rlm);
	if (sourceHandledThisLoop && stopAfterHandle) {///source处理完毕&处理完毕需求间断runloop
	    retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {///现已超时
            retVal = kCFRunLoopRunTimedOut;
	} else if (__CFRunLoopIsStopped(rl)) {///通过`CFRunLoopStop`函数设置rl情况为STOPPED
            __CFRunLoopUnsetStopped(rl);
	    retVal = kCFRunLoopRunStopped;
	} else if (rlm->_stopped) {///runLoopMode现已间断
	    rlm->_stopped = false;
	    retVal = kCFRunLoopRunStopped;
	} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { ///rkm为空,
            ///没有一个timer或source0或source1 或许rl没有block需求实行,并且不是主队伍
	    retVal = kCFRunLoopRunFinished;
	}
        //...
    } while (0 == retVal);
     ///...
    return retVal;
}

RunLoop工作函数内部是一个do-while循环,让线程继续工作,接收工作,处理工作;RunLoop定义了一些情况,当它在特定RunLoopMode下工作时,可以向该Mode下注册的查询者发送消息;

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

苹果文档中总结了RunLoop工作时的工作的实行序列,大致如下:

  1. 告知查询者,RunLoop即将进入
  2. 告知查询者,RunLoop即将触发Timer
  3. 告知查询者,RunLoop即将处理Source0(非mach_port
  4. 触发任何预备触发的非依据端口的Source0输入源
  5. 假设依据mach_port的输入源Source1现已安排妥当等候触发,则跳转第9步处理Source1.
  6. 告知查询者,RunLoop即将进入休眠
  7. 让线程进入休眠,直到发生以下工作之一:
    • 依据mach_port的输入源Source1发生;
    • 定时器触发;
    • RunLoop设置的timeout收效,工作即将结束;
    • RunLoop被显式唤醒,调用CFRunLoopWakeUp;
  8. 告知查询者,RunLoop即将被唤醒
  9. 唤醒后,处理待处理的工作:
    • 用户定义的定时器发起,跳转第2步,处理定时器工作,从头初步循环(2 ~ 9
    • 处理依据端口的输入源,传递收到的消息。
    • RunLoop被显式唤醒但还没超时,跳转第2步,从头初步循环(2 ~ 9
  10. 告知查询者,RunLoop退出

RunLoop退出时,工作函数会回来下列枚举值:

typedef CF_ENUM(SInt32, CFRunLoopRunResult) {
    kCFRunLoopRunFinished = 1,
    kCFRunLoopRunStopped = 2,
    kCFRunLoopRunTimedOut = 3,
    kCFRunLoopRunHandledSource = 4 
};
  1. Source处理完毕并且需求当即间断runloop时,回来kCFRunLoopRunHandledSource,退出RunLoop;
  2. RunLoop设置的timeout收效,回来kCFRunLoopRunTimedOut,退出`RunLoop;
  3. 显式调用CFRunLoopStop函数,设置RunLoop情况为STOPPED,回来kCFRunLoopRunStopped,退出RunLoop;
  4. RunLoop工作的Mode是间断情况,回来kCFRunLoopRunStopped,退出RunLoop;
  5. RunLoop工作的Mode为空,没有timersource0source1,或许runloop没有需求实行的block回来kCFRunLoopRunFinished,退出RunLoop;

毕竟再通过一张图,总结下RunLoop内部工作逻辑,大致如下:

谈谈 iOS RunLoop 底层

RunLoop的运用

Oberserver

CoreFoundationObserver的结构:

struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount; ///被添加到Rl几回
    CFOptionFlags _activities;//需求查询RL哪些情况
    CFIndex _order;///情况工作触发时,顺次告知的查询者,值越小优先级越高	
    CFRunLoopObserverCallBack _callout;	//情况工作触发时的回调
    CFRunLoopObserverContext _context;// 上下文	
};
typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;

查询者,可以被添加到RunLoop的多个Mode下,同一个Mode下可以有多个Oberserver,当工作触发时,查询者是依据_order值从小到大的次第进行工作回调的。

示例: 创建滑动视图并为主线程RunLoopkCFRunLoopCommonModes (NSRunLoopCommonModes)添加Observer,查询RunLoop的切换。

///设置查询者
- (void)addObserverForMainRunLoop {
    /*
     UITrackingRunLoopMode,GSEventReceiveRunLoopMode,
     kCFRunLoopDefaultMode,kCFRunLoopCommonModes
     */
    void *info = (__bridge_retained void *)self;
    CFRunLoopObserverContext context = {0,info,NULL,NULL,NULL};
   //一个优先级索引,指示处理工作循环查询者的次第。在给定的工作循环方式下,当多个工作循环查询者被调度在同一活动阶段时,查询者按此参数的递加次第进行处理。传递 0,除非有理由不这样
    CFRunLoopObserverRef changeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault ,
                                                                  kCFRunLoopAllActivities,
                                                                  YES,
                                                                  0,
                                                                  &_runLoopObserverCallBack,
                                                                  &context);
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), changeObserver, kCFRunLoopCommonModes);
    CFRelease(changeObserver);
}
///回调函数
void _runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    ///获取mode称谓
    NSString* mode = (__bridge NSString*)CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
    if (info) {//桥接info为oc方针 
    }
    switch (activity) {
        //do somthing...
    }
}

Source0

CoreFoundationSource的结构:

struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order;	///同observer
    CFMutableBagRef _runLoops;
    union {
	CFRunLoopSourceContext version0; //source0
        CFRunLoopSourceContext1 version1; //source1
    } _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 {
    ///...同`CFRunLoopSourceContext`前7个特点
#if TARGET_OS_OSX || TARGET_OS_IPHONE
    mach_port_t	(*getPort)(void *info);
    void *	(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
    ///...
#endif
} CFRunLoopSourceContext1;
typedef struct __CFRunLoopSource * CFRunLoopSourceRef;

苹果系统定义了一些API,底层是依据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 

不过需求留心当调用主线程->子线程序系列方法时,务必要保证子线程的RunLoop是打开的,否则不会收效。

示例: 我们依据Souce0简略仿照下performSelector从主线程发送消息给子线程。

///1.打开子线程
 _subthread = [[NSThread alloc]initWithTarget:self selector:@selector(subthreadOperation) object:nil];
///2.子线程的方法中,添加一个`Souce0`工作,并打开`RL`
- (void)subthreadOperation {
    ///3.保存子线程的runloop
    _subRunLoop = CFRunLoopGetCurrent();
    ///4.创建&添加source0
    void *info = (__bridge_retained void*)self;
    CFRunLoopSourceContext context = {0,info,NULL,NULL,NULL,NULL,NULL,&schedule,&cancel,&perform};
    _source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
    CFRunLoopAddSource(_subRunLoop, _source, kCFRunLoopDefaultMode);
    ///工作RL
    [[NSRunLoop currentRunLoop] run];
}
///与Source0相关的回调
void schedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode) {
    ///source0已参与子线程的runloop中
}
void cancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode) {
    ///source0已从子线程的runloop中移除
}
void perform(void *info) {
   ///桥接info,获取来自主线程的消息
}
///5.主线程触发Source0工作
- (void)buttonAction:(id)sender {
    //5.1 触发source
    CFRunLoopSourceSignal(_source);
    ///5.2 唤醒runLoop
    CFRunLoopWakeUp(_subRunLoop);
}
///6.移除Source0,退出子线程RL
Boolean contain = CFRunLoopContainsSource(_subRunLoop, _source, kCFRunLoopDefaultMode);
if (contain) {
    //6.1移除source
    CFRunLoopRemoveSource(_subRunLoop, _source, kCFRunLoopDefaultMode);
    ///6.2间断runLoop
    CFRunLoopStop(_subRunLoop);
}

总结: 所谓Souce0只不过是被包装的带有上下文的函数,需求主动触发,这个函数才会被实行。

Source1

Sorce1是依据mach_port的工作,它是内核工作,苹果系统的内核是XNU混合内核,包含了Mach内核和BSD内核,BSD首要供应在Mach之上标准化的APIMach才是中心,担任线程与进程办理、虚拟内存办理、进程通讯与消息传递、任务调度等。

依据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)完结工作传递。骗局毕竟的用处,是在用户程序和内核之间供应一个像进程一样的接口,称为系统调用

macOS系统中,可以运用依据mach_portSource1完结进程通讯。

示例: 创建一个子线程,通过Source1树立主线程与子线程信道,完结双向通讯。

///1.打开子线程
 _subthread = [[NSThread alloc]initWithTarget:self selector:@selector(launchThreadWithPort:) object:nil];
///2.子线程的方法中,添加一个`Souce1`工作,并打开`RL`
- (void)launchThreadWithPort:(NSPort*)port {
    @autoreleasepool {
        ///3.创建&添加Source1
        void *info = (__bridge_retained void*)self;
        CFMessagePortContext portcontext = {0,info,NULL,NULL,NULL};
        Boolean shouldFreeInfo;
        CFMessagePortRef mach_port = CFMessagePortCreateLocal(kCFAllocatorDefault,  CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("com.qishare.sub_mach_port")), &_messagePortCallBack, &portcontext, &shouldFreeInfo);
        ///保存端口,树立双向信道
        _subPort = mach_port;
        if (mach_port != NULL) {
            CFRunLoopSourceRef source1 = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, mach_port, 0);
            if (source1 != NULL) {
                CFRunLoopAddSource(CFRunLoopGetCurrent(), source1, kCFRunLoopDefaultMode);
                CFRunLoopRun();
                CFRelease(source1);
                CFRelease(mach_port);
            }
        }
    }
}
///3.1`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;
    NSString *tip =  msgid == 1002 ? @"主线程" : @"子线程";
    NSLog(@"%@:%@,收到数据:%@", tip,[NSThread currentThread],message);
    return NULL;
}
///5.消息发送:子线程->主线程
[self performSelector:@selector(sendMsgToMainThread) onThread:_subthread withObject:nil waitUntilDone:NO];
///5.1构建消息  10002 代表 主线程->子线程
NSData *data = [@"你好,我来自子线程✈️" dataUsingEncoding:NSUTF8StringEncoding];
CFDataRef msgData = (__bridge_retained CFDataRef)data;
///5.2发送消息
CFMessagePortSendRequest(_mainPort, 1002, msgData, 0.1, 0.0, NULL, NULL);
CFRelease(msgData);
///6.消息发送:主线程->子线程
CFStringRef message = CFSTR("你好,我来自主线程");
CFDataRef outData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, message, kCFStringEncodingUTF8, 0);
///发送消息, 10001 代表 主线程->子线程
CFMessagePortSendRequest(_subPort, 1001, outData, 0.1, 0.0, NULL, NULL);
///释放资源
CFRelease(outData);
CFRelease(message);

Timer

CoreFoundationtimer的结构:

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;

苹果系统中定义的延迟调用API,底层就是依据Timer完结的:

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

示例1: 创建子线程打开一个CoreFoundationtimer:

///1.打开子线程
  _subthread = [[NSThread alloc]initWithTarget:self selector:@selector(subthreadOperation) object:nil];
///2.子线程下创建&添加timer
- (void)subthreadOperation {
   ///保存子线程`RL`
    _subRunLoop = CFRunLoopGetCurrent();
    @autoreleasepool {
        __weak typeof(self)weakSelf = self;
        CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, 0, 1, 0, 0, ^(CFRunLoopTimerRef timer) {
          ///定时器工作
        });
        _cftimer = timer;
        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
        CFRunLoopRun();
    }
    ///间断时调用
    NSLog(@"RunLoop:我要走向消除,不要拦我呀!");
}
///3.间断定时器&子线程的RL
- (void)stopCFTimerLoop {
    ///3.1移除`timer`
    CFRunLoopRemoveTimer(_subRunLoop, _cftimer, kCFRunLoopDefaultMode);
    ///3.2间断RL
    CFRunLoopStop(_subRunLoop);
    CFRelease(_cftimer);
}

示例2: 创建子线程打开一个NSFoundationtimer:

///1.打开子线程
  _subthread = [[NSThread alloc]initWithTarget:self selector:@selector(subthreadOperation) object:nil];
///2.子线程下创建&添加timer
- (void)subthreadOperation {
    if (_timer) {
        [_timer invalidate];
        _timer = nil;
    } else {
        _timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
           ///定时器工作
        }];
        [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
        ///MARK: 定时器间断时,子线程Runloop退出的考虑?
        ///[[NSRunLoop currentRunLoop]run];
        ///主张运用这种方法
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        NSLog(@"RunLoop:我要走向消除,你不要拦我呀!");
    }
}
///3.间断定时器&子线程的RL
- (void)stopTimerLoop {
    ///This method is the only way to remove a timer from an NSRunLoop object.
    [_timer invalidate];
    ///间断
    CFRunLoopStop(_subRunLoop);    
}

示例2打开子线程的RunLoop选用[[NSRunLoop currentRunLoop]run]的方法,在不调用[_timer invalidate]的情况下,RunLoop是无法退出的,而runMode:beforeDate:是可以的。这种方法可以保证我们在RunLoop中有其他工作源时并且未移除的情况下,能退出RunLoop

总结一下就是runMode:beforeDate:在不移除工作的情况下,能显式退出,而[[NSRunLoop currentRunLoop]run]在不移除工作的情况下,不能显式退出。

子线程保活

子线程保活,实质就是打开子线程的RunLoop。但打开子线程的RunLoop前,必须要保证RunLoop中至少有个TimerSouce0Source1

最简略的保活方法:

///1.打开子线程
  _subthread = [[NSThread alloc]initWithTarget:self selector:@selector(subthreadOperation) object:nil];
///2.子线程下创建&添加timer
- (void)subthreadOperation {
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    _shouldKeepRunning = YES;
    do {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    } while (_shouldKeepRunning);   
}
///3.间断
- (void)stopLoop {
    CFRunLoopStop(_subRunLoop);  
    _shouldKeepRunning = NO;
}

参考资料

developer.apple.com/library/arc…

blog.ibireme.com/2015/05/18/…

github.com/apple/darwi…

opensource.apple.com/source/CF/C…