RunLoop是什么?
RunLoop
是iOS
/Mac OS
开发中比较重要的知识点,它贯穿程序工作的整个进程。它是线程基础架构的一部分,是一种保证线程循环处理工作而不会退出的机制。一同也担任办理线程需求处理的工作,让线程有事儿时繁忙,没事儿时休眠。
每个线程都有一个相关的RunLoop
方针,子线程的RunLoop
是需求手动打开的,主线程的RunLoop
作为运用发起的一部分由系统主动打开。
iOS
/Mac OS
供应了NSRunLoop
和CFRunLoopRef
两个方针,帮忙我们配置和办理线程的RunLoop
。CFRunLoopRef
供应纯C
完结并且线程安全的API;NSRunLoop
是依据CFRunLoopRef
封装的面向方针的API,这个API不是线程安全的。
RunLoop与线程的联络
苹果是不主张我们自己创建RunLoop
方针,但是我们可以通过下列方法获取特定线程下的RunLoop
方针:
[NSRunLoop currentRunLoop];
[NSRunLoop mainRunLoop];
//CoreFoundation
CFRunLoopGetMain();
CFRunLoopGetCurrent();
CoreFoundation
是开源的(下载地址),我们可以查看CFRunLoopRef
的关于CFRunLoopGetMain
和CFRunLoopGetCurrent
的完结:
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
源码查看可前往此处。
总结:
-
RunLoop
与线程之间是一一对应的 - 当线程需求获取对应的
RunLoop
时,才会创建RunLoop
方针 - 线程销毁的时分会销毁
RunLoop
方针
RunLoop的相关类
CoreFoundation
中与RunLoop
有关的5
个结构体:
CFRunLoopRef //runLoop方针
CFRunLoopModeRef //runLoop工作的方式
CFRunLoopTimerRef// 依据时间的触发器
CFRunLoopSourceRef//工作源,source0:自定义工作输入源 和 source1 :依据mach内核端口的工作源
CFRunLoopObserverRef //用于监听runLoop工作情况的查询者
它们之间的联络如下:
具体可通过打印[NSRunLoop currentRunLoop]
查看,也可通过查看CFRunLoopRef
和CFRunLoopModeRef
的结构定义。两者的结构定义如下:
///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
相关的输入源source0
、source1
才干被处理,相同的,监听RunLoop
的工作,只要与该Mode
相关的observers
才干收到告知。
///`RunLoop`指定`Mode`工作
CFRunLoopRunResult CFRunLoopRunInMode(CFRunLoopMode mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);
程序在工作的进程中,会处理依据时间的、系统的、用户的工作,这些工作在程序工作期间有着不同的优先级,为了满意运用层依据优先级对这些工作的办理,系统选用RunLoopMode
对这些工作分组,然后交由RunLoop
去办理。除了系统定义的默许方式和常用方式,我们也可以自定义方式,但是自定义的方式中必须有相关的工作,否则自定义方式没有任何意义。
_commonModeItems
与_commonModes
是kCFRunLoopCommonModes (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
方式下,毕竟定时器会被添加到kCFRunLoopDefaultMode
和UITrackingRunLoopMode
下;当然也可以自行添加到这两个方式中。
查看CoreFoundation
中CFRunLoopAddTimer
方法的完结,可以更深化的了解_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
函数发起。
通进程序发起时,函数的调用栈,发现调用了CFRunLoopRunSpecific
;
并且Mode
之间的切换通过LLDB
调试方法:b CFRunLoopRunSpecific
和 b __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
工作时的工作的实行序列,大致如下:
- 告知查询者,
RunLoop
即将进入 - 告知查询者,
RunLoop
即将触发Timer
- 告知查询者
,RunLoop
即将处理Source0
(非mach_port
) - 触发任何预备触发的非依据端口的
Source0
输入源 - 假设依据
mach_port
的输入源Source1
现已安排妥当等候触发,则跳转第9步处理Source1
. - 告知查询者,
RunLoop
即将进入休眠 - 让线程进入休眠,直到发生以下工作之一:
- 依据
mach_port
的输入源Source1
发生; - 定时器触发;
-
RunLoop
设置的timeout
收效,工作即将结束; -
RunLoop
被显式唤醒,调用CFRunLoopWakeUp
;
- 依据
- 告知查询者,
RunLoop
即将被唤醒 - 唤醒后,处理待处理的工作:
- 用户定义的定时器发起,跳转第2步,处理定时器工作,从头初步循环(
2 ~ 9
) - 处理依据端口的输入源,传递收到的消息。
-
RunLoop
被显式唤醒但还没超时,跳转第2步,从头初步循环(2 ~ 9
)
- 用户定义的定时器发起,跳转第2步,处理定时器工作,从头初步循环(
- 告知查询者,
RunLoop
退出
RunLoop
退出时,工作函数会回来下列枚举值:
typedef CF_ENUM(SInt32, CFRunLoopRunResult) {
kCFRunLoopRunFinished = 1,
kCFRunLoopRunStopped = 2,
kCFRunLoopRunTimedOut = 3,
kCFRunLoopRunHandledSource = 4
};
-
Source
处理完毕并且需求当即间断runloop
时,回来kCFRunLoopRunHandledSource
,退出RunLoop
; -
RunLoop
设置的timeout
收效,回来kCFRunLoopRunTimedOut
,退出`RunLoop; - 显式调用
CFRunLoopStop
函数,设置RunLoop
情况为STOPPED
,回来kCFRunLoopRunStopped
,退出RunLoop
; -
RunLoop
工作的Mode
是间断情况,回来kCFRunLoopRunStopped
,退出RunLoop
; -
RunLoop
工作的Mode
为空,没有timer
或source0
或source1
,或许runloop
没有需求实行的block
回来kCFRunLoopRunFinished
,退出RunLoop
;
毕竟再通过一张图,总结下RunLoop
内部工作逻辑,大致如下:
RunLoop的运用
Oberserver
CoreFoundation
中Observer
的结构:
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
值从小到大的次第进行工作回调的。
示例: 创建滑动视图并为主线程RunLoop
的kCFRunLoopCommonModes (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
CoreFoundation
中Source
的结构:
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
之上标准化的API
,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
)完结工作传递。骗局毕竟的用处,是在用户程序和内核之间供应一个像进程一样的接口,称为系统调用
macOS
系统中,可以运用依据mach_port
的Source1
完结进程通讯。
示例: 创建一个子线程,通过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
CoreFoundation
中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;
苹果系统中定义的延迟调用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: 创建子线程打开一个CoreFoundation
的timer
:
///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: 创建子线程打开一个NSFoundation
的timer
:
///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
中至少有个Timer
、Souce0
或Source1
。
最简略的保活方法:
///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…