Runloop
Runloop 不仅仅是一个运行循环,而且是一个对像,对像里面提供很多东西。(do-while循环)
提供一个入口,让程序进入do...while循环,保证应用程序不被退出。
runloop的官方文档是在thread里面的一个小分支,runloop和线程是息息相关的。
runloop是一种消息机制的处理模式,例如屏幕点击事件或者timer都会交给timer去处理。
runloop的作用:
1. 保证程序的持续运行。
2. 处理APP中的各种事件(触摸,定时器,performSelector等)。
3. 节省cpu资源,提供程序的性能:改做事就做事,改休息就休息。
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
官方文档:
APP启动的时候,runloop就开始循环
在main函数中返回0,APP会直接退出,不会继续运行。
调用timer,查看调用堆栈,__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ 为当前runloop类型
搜索__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__源码
调用perforSelector,也会使用__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ [self performSelector:@selector(fire) withObject:nil afterDelay:1.0];,
调用主队列多线程__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
调用block,__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
Runloop 的item
block应用:__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
调用timer:__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
响应source0:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
响应source1: __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
GCD主队列:__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
observer源:__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
线程和runloop是一一对应的关系:
线程和runloop 是通过key-value的方式进行一一对应
// 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);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
// 给dict setValue 线程指针 forKey mainLoop 所以线程和runloop是一一对应的关系
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
//这个也是根据线程指针从dict里面去除runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
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())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
线程会自动销毁,只打印了一次 ,线程和runloop是一一对应的,但是子线程 是默认不开启runloop的,需要在子线程内部加 [[NSRunLoop currentRunLoop] run];
如果需要退出线程在线程内部 [NSThread exit]; 退出runloop,退出之后子线程就没有意义了就会销毁
timer的运行是依赖runloop的,runloop退出的话,timer就失效了
源码分析:
//CFRunloopMode 研究
CFRunLoopRef lp = CFRunLoopGetCurrent();
CFRunLoopMode model = CFRunLoopCopyCurrentMode(lp);
NSLog(@"model == %@", model);
CFArrayRef modeArray = CFRunLoopCopyAllModes(lp);
NSLog(@"modelArray == %@", modeArray);
NSRunLoop 是由CFRunLoopRef构建
CFRunLoopRef和线程是一一对应关系
struct __CFRunLoop { RunLoop是个结构体,是个对象
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */ lock锁
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp 激活端口
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread;
uint32_t _winthread; 支持windows
CFMutableSetRef _commonModes; set类型,说明会有多个Modes
CFMutableSetRef _commonModeItems; set类型,说明会有多个items
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
集合类型,说明每个Mode,都会有多个sources0,sources1,observers,timers
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
一个runloop只能在一个mode下运行,但是可以在不同的mode之间切换。
验证当前在defaultMode,并且有4个mode。
下面分析timer是如何加到runloop中的:
__CFRunLoopDoBlocks(rl, rlm); //rl是runloop, rlm是runloopMode,意思是在rlm模式先有哪些runLoop需要执行
通过链表遍历items,如果有
简述下刚才timer的流程:
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
// timer 加入的mode 和 我们现在runloop的mode 相等
// curr->_mode = kCFRunLoopCommonModes 相等
// 事务就可以执行
doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
}
demo:timer 底层cf fxRunLoopTimerCallBack为一个c语言方法
//只要runloop状态发生改变,就会回调。fxRunLoopObserveCallBack
source :
联合体虽然是同一个指针,但是不会覆盖,有位域来控制
source0 只包含一个回调函数指针(标记 signal待处理,wakeup唤醒 runloop处理事件)
任何的触摸事件和响应事件都是source0
source1 包含mach_port 和回调函数指针 (线程之间通信 )
source1 依赖于port,主要使用port于线程间通信(很少用,用起来麻烦),已经内核管理
components里面为传过来的信息