1.do-while 循环
源码
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
结论
可以看到,runloop在run起来后其实是用一个do-while循环实现的,不同的是,runloop可以做到不需要处理事务的时候就sleep,需要的时候就work。其作用总结就是:
保持程序的持续运行
处理App中各种事件(触摸、定时器、performSelector)
节省CPU资源、提供程序的性能:该做事做事,该休息休息
2.与线程的关系
源码
##### mainRunloop
CFRunLoopRef mainRunloop = CFRunLoopGetMain();
##### CFRunLoopGetMain()函数内部实现
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
//可以看到传了一个主线程参数进入
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
##### _CFRunLoopGet0()函数内部实现
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
if (!__CFRunLoops) {
__CFSpinUnlock(&loopsLock);
//建立全局的字典用来存储 线程和runloop的关系
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//通过__CFRunLoopCreate()函数创建runloop实例对象
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//将mainLoop 和 mainThreadb存入字典
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}
//主线程的loop在上面就已经创建了,所以如果获取不到,那肯定是子线程的loop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
//没有loop,那就创建子线程的loop对象
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//子线程和其runloop也会被存在这个全局的字典中
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFSpinUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
//返回runloop
return loop;
}
结论
系统会创建一个全局的可变字典CFMutableDictionaryRef dict,储存线程和runloop;
主线程的runloop是默认创建的
子线程的runloop默认不启动,需要主动获取
3.内部的mode-item关系
源码
#### __CFRunLoop对象内部结构
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__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;
//有好多的Modes、Items
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
//但是只有一个currentMode
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
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];
//有好多的事件源 _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 */
};
结论
__CFRunLoop对象内部包含多个modes
而每个mode中又包含了多个items
items就是事件源_sources0、_sources1、_observers、_timers...
但是__CFRunLoop对象同时只能持有一种mode:_currentMode
runloop对象结构图
![](https://i-blog.csdnimg.cn/blog_migrate/18dfc3720b1cbd3c79bda42484492e73.png)
4.Runloop的item
runloop有六大事务item,这些item都是由runloop调用的。先看代码
- (void)timerItem{
//__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"test timer");
}];
}
等程序运行起来在断点看内存堆栈调用
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2
* frame #0: 0x000000010b59a240 01-Runloop初探`__28-[ViewController sourceDemo]_block_invoke(.block_descriptor=0x0000600000658318, timer=0x0000600000f58010) at ViewController.m:26
frame #1: 0x000000010b987413 Foundation`__NSFireTimer + 72
frame #2: 0x000000010bfb3b94 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
frame #3: 0x000000010bfb3882 CoreFoundation`__CFRunLoopDoTimer + 1026
frame #4: 0x000000010bfb2eda CoreFoundation`__CFRunLoopDoTimers + 266
frame #5: 0x000000010bfadc4e CoreFoundation`__CFRunLoopRun + 2238
frame #6: 0x000000010bfad066 CoreFoundation`CFRunLoopRunSpecific + 438
frame #7: 0x00000001154c6bb0 GraphicsServices`GSEventRunModal + 65
frame #8: 0x000000010f2e0d4d UIKitCore&#