Runloop:原理篇

目录
一,基本知识
二,Runloop对象
三,底层结构
四,Mode
五,运行流程
六,休眠的原理
一,基本知识
1,含义
运行循环,在程序运行过程中循环的处理事件
2,作用
- 保证程序能够持续的运行
1>没有
runloop
// 执行完return,程序立即退出 int main(int argc, char * argv[]) { @autoreleasepool { NSLog(@"hello world!"); return 0; } }
2>有
runloop
// 不会执行return,程序不会退出 int main(int argc, char * argv[]) { @autoreleasepool { int value = 0; // 函数内部会启动runloop,runloop会让代码阻塞在此 value = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); return value; } }
3>伪代码
int main(int argc, char * argv[]) { @autoreleasepool { int value = 0; do { // runloop处理事件或休眠 } while (1); return value; } }
-
处理程序运行过程中的各种事件
-
节省资源:有事就处理,没事就休眠
二,Runloop对象
1,CFRunLoopRef
(源码下载地址)
// 获取主线程的runloop对象
CFRunLoopRef main = CFRunLoopGetMain();
// 获取当前线程的runloop对象
CFRunLoopRef current = CFRunLoopGetCurrent();
2,NSRunLoop
(对CFRunLoopRef
的封装)
// 获取主线程的runloop对象
NSRunLoop *main = [NSRunLoop mainRunLoop];
// 获取当前线程的runloop对象
NSRunLoop *current = [NSRunLoop currentRunLoop];
3,与线程的关系
- 说明
1>每条线程都有一个
runloop
对象2>线程刚创建时并不会创建
runloop
对象,而是在第一次获取它时创建的3>主线程的
runloop
对象会在程序启动时自动创建,子线程的需要手动创建4>
runloop
对象会在线程销毁时自动销毁5>
runloop
对象存储在一个全局的字典里,线程作为key
,runloop
对象作为value
- 源码
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL;
// 调用_CFRunLoopGet0
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np());
return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
// 调用_CFRunLoopGet0
return _CFRunLoopGet0(pthread_self());
}
// 简化代码
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
// 用线程从字典中取出runloop对象
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
// 如果字典中没有该线程对应的runloop对象
if (!loop) {
// 创建runloop对象
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
// 以线程作为key把创建的runloop对象存储到字典中
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
return loop;
}
三,底层结构
1,CFRunLoopRef
typedef struct __CFRunLoop * CFRunLoopRef;
// 简化代码
struct __CFRunLoop {
pthread_t _pthread; // 线程对象
CFMutableSetRef _commonModes; // 用kCFRunLoopCommonModes标记的mode名称
CFMutableSetRef _commonModeItems; // 所有添加在kCFRunLoopCommonModes下的事件
CFRunLoopModeRef _currentMode; // 当前mode
CFMutableSetRef _modes; // 所有mode
};
2,CFRunLoopModeRef
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
// 简化代码
struct __CFRunLoopMode {
CFStringRef _name; // 名称
CFMutableSetRef _sources0; // CFRunLoopSourceRef集合
CFMutableSetRef _sources1; // CFRunLoopSourceRef集合
CFMutableArrayRef _observers; // CFRunLoopObserverRef数组
CFMutableArrayRef _timers; // CFRunLoopTimerRef数组
};
3,关系图

4,说明
-
一个
runloop
包含若干个mode
,每个mode
又包含若干个source0
/source1
/observer
/timer
-
mode
代表runloop
的运行模式,runloop
每次只能选择一个mode
来运行 -
如果需要切换
mode
,runloop
会先退出,然后选择一个mode
重新进入 -
如果
mode
是空的,runloop
会立即退出
四,Mode
1,作用
将不同类型的source0
/source1
/observer
/timer
分隔开,这样runloop
在某种mode
下运行时,只需要处理一种类型的事件,效率会比较高
2,类型
-
kCFRunLoopDefaultMode
:默认mode
,主线程一般都在这个mode
下运行 -
UITrackingRunLoopMode
:跟踪界面滑动的mode
,保证界面滑动时不受其他mode
的影响 -
UIInitializationRunLoopMode
:程序启动时会先进入这个mode
,启动完成后会切换到kCFRunLoopDefaultMode
,此mode
便不再使用 -
GSEventReceiveRunLoopMode
:接收系统事件的mode
,一般用不到 -
kCFRunLoopCommonModes
:不是一个真正的mode
,只是用来标记
kCFRunLoopDefaultMode
和UITrackingRunLoopMode
的
3,CFRunLoopSourceRef
(事件源)
-
source0
:非基于port
,用户触发的事件,需要手动唤醒线程

-
source1
:基于port
,其他线程发送的消息,能主动唤醒线程
4,CFRunLoopObserverRef
(观察者)
// 监听runloop的运行状态
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"进入runloop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理sources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"刚被唤醒");
break;
case kCFRunLoopExit:
NSLog(@"退出runloop");
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
CFRelease(observer);
// 打印
即将处理timers
即将处理sources
即将处理timers
即将处理sources
即将休眠
刚被唤醒
即将处理timers
即将处理sources
即将休眠
刚被唤醒
即将处理timers
即将处理sources
即将休眠
5,CFRunLoopTimerRef
(定时器)

6,切换mode
- (void)viewDidLoad {
[super viewDidLoad];
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"进入runloop---%@", CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent()));
break;
case kCFRunLoopExit:
NSLog(@"退出runloop---%@", CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent()));
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
CFRelease(observer);
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
NSLog(@"%s", __func__);
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate {
NSLog(@"%s", __func__);
}
// 打印
-[ViewController scrollViewWillBeginDragging:]
退出runloop---kCFRunLoopDefaultMode
进入runloop---UITrackingRunLoopMode
-[ViewController scrollViewDidEndDragging:willDecelerate:]
退出runloop---UITrackingRunLoopMode
进入runloop---kCFRunLoopDefaultMode
五,运行流程
1,官方图解

2,自定义图解

3,源码
- 入口

CFRunLoopRunSpecific
// 简化代码
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
// 通知observers:进入runloop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 循环处理事件
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知observers:退出runloop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
__CFRunLoopRun
// 简化代码
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
// 通知observers:即将处理timers
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 通知observers:即将处理sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 处理blocks
__CFRunLoopDoBlocks(rl, rlm);
// 处理sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
// 可能会再次处理blocks
__CFRunLoopDoBlocks(rl, rlm);
}
// 是否存在sources1
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
// 通知observers:即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
// 等待消息唤醒(内部会调用mach_msg函数)
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
// 通知observers:刚被唤醒
__CFRunLoopUnsetSleeping(rl);
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:
if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { // 被timer唤醒
// 处理timers
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
} else if (livePort == dispatchPort) { // 被GCD唤醒
// 处理GCD
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else { // 被source1唤醒
// 处理sources1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply);
}
// 处理blocks
__CFRunLoopDoBlocks(rl, rlm);
// 是否继续处理事件
if (sourceHandledThisLoop && stopAfterHandle) { // 否
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) { // 否
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) { // 否
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { // 否
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}
六,休眠的原理
1,说明
-
当没有消息时,线程会从用户态切换到内核态,开始休眠等待
-
当有消息时,线程会从内核态切换到用户态,开始处理事件
-
监听消息和转换状态都是由
mach_msg
函数完成的
2,图解
