什么是RunLoop
顾名思义,运行循环,在程序运行过程中循环做一些事情。如果没有RunLoop,程序执行完毕就会立即退出,如果有了RunLoop,程序并不会马上退出,而是保持运行状态。
RunLoop基本作用:
- 保持程序持续运行
- 处理App中的各种事件(比如:触摸事件,定时器事件,Selector事件等)
- 节省CPU资源,提高程序性能:该做事时做事,该休息时休息
- ......
RunLoop在哪里开启
UIApplicationMain函数内启动了Runloop,程序不会马上退出,而是保持运行状态。因此每一个应用必须要有一个runloop,
我们知道主线程一开起来,就会跑一个和主线程对应的RunLoop,那么RunLoop一定是在程序的入口main函数中开启。
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
进入UIApplicationMain
UIKIT_EXTERN int UIApplicationMain(int argc, char * _Nullable argv[_Nonnull], NSString * _Nullable principalClassName, NSString * _Nullable delegateClassName);
发现它返回的是一个int数,那么我们对main函数做一些修改
int main(int argc, char * argv[]) {
@autoreleasepool {
NSLog(@"开始");
int re = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
NSLog(@"结束");
return re;
}
}
运行程序,发现只会打印开始,并不会打印结束,这说明在UIApplicationMain函数中,开启了一个和主线程相关的RunLoop,导致UIApplicationMain不会立即返回,一直在运行中,也就保证了程序的持续运行。
我们来看到RunLoop的源码
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确实是do while通过判断result的值实现的。因此,我们可以把RunLoop看成一个死循环。
RunLoop对象
- iOS中有2套API来访问和使用RunLoop
- Foundation:NSRunLoop
- Core Foundation:CFRunLoopRef
- NSRunLoop和CFRunLoopRef都代表着RunLoop对象
- NSRunLoop是基于CFRunLoopRef的一层OC包装
- CFRunLoopRef是开源的 https://opensource.apple.com/tarballs/CF/
RunLoop与线程
- 每条线程都有唯一的一个与之对应的RunLoop对象
- RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
- 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
- RunLoop会在线程结束时销毁
- 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
下面通过看 CFRunloopRef 源码对前面三条结论进行证明
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;
}
CFRunLoopGetCurrent
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
可以看到 CFRunLoopGetMain 和 CFRunLoopGetCurrent 调用了 _CFRunLoopGet0
函数,并把主线程或当前线程作为参数传递进去。
_CFRunLoopGet0
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());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
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;
}
通过源码可以看到,首先先将传入的线程参数跟kNilPthreadT 进行比较
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
在 CFRunLoop 源码可以找到 kNilPthreadT 的定义
#if DEPLOYMENT_TARGET_WINDOWS
static pthread_t kNilPthreadT = { nil, nil };
#else
static pthread_t kNilPthreadT = (pthread_t)0;
#endif
可以看到 kNilPthreadT 表示线程 0,即主线程,所以上面源码通过将传入线程跟主线程进行比较,看是不是主线程,如果是,将主线程(pthread_main_thread_np())赋值给传入的线程
接着判断 __CFRunLoops
是否存在,如果不存在,则先创建用于存储 Runloop 的全局Dictionary,再创建主线程对应的 mainLoop ,并以用 pthreadPointer 对主线程做一些处理得到的值作为 key,将mainLoop 存储到全局Dictionary 中
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
接着将 __CFRunLoops
和 pthreadPointer 对线程 t 做一些处理得到的值 作为参数传入 CFDictionaryGetValue
函数中获取线程 t 对应的 loop
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);
}
如果 loop 不存在,创建一个newLoop, 然后再一次调用CFDictionaryGetValue
看一下现在是否已经存在对应的 loop 了,如果依旧不存在,将 newLoop 存入全局Dictionary,并将要返回的 loop 值赋值为newLoop,最后将 loop 返回
写一下代码熟悉加深理解
// MJPermenantThread.h
#import <Foundation/Foundation.h>
typedef void (^MJPermenantThreadTask)(void);
@interface MJPermenantThread : NSObject
/**
开启线程
*/
//- (void)run;
/**
在当前子线程执行一个任务
*/
- (void)executeTask:(MJPermenantThreadTask)task;
/**
结束线程
*/
- (void)stop;
@end
// MJPermenantThread.m
#import "MJPermenantThread.h"
/** MJThread **/
@interface MJThread : NSThread
@end
@implementation MJThread
- (void)dealloc
{
NSLog(@"%s", __func__);
}
@end
/** MJPermenantThread **/
@interface MJPermenantThread()
@property (strong, nonatomic) MJThread *innerThread;
@end
@implementation MJPermenantThread
#pragma mark - public methods
- (instancetype)init
{
if (self = [super init]) {
self.innerThread = [[MJThread alloc] initWithBlock:^{
NSLog(@"begin----");
// 创建上下文(要初始化一下结构体)
CFRunLoopSourceContext context = {0};
// 创建source
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
// 往Runloop中添加source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 销毁source
CFRelease(source);
// 启动
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
// while (weakSelf && !weakSelf.isStopped) {
// // 第3个参数:returnAfterSourceHandled,设置为true,代表执行完source后就会退出当前loop
// CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
// }
NSLog(@"end----");
}];
[self.innerThread start];
}
return self;
}
//- (void)run
//{
// if (!self.innerThread) return;
//
// [self.innerThread start];
//}
- (void)executeTask:(MJPermenantThreadTask)task
{
if (!self.innerThread || !task) return;
[self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}
- (void)stop
{
if (!self.innerThread) return;
[self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}
- (void)dealloc
{
NSLog(@"%s", __func__);
[self stop];
}
#pragma mark - private methods
- (void)__stop
{
CFRunLoopStop(CFRunLoopGetCurrent());
self.innerThread = nil;
}
- (void)__executeTask:(MJPermenantThreadTask)task
{
task();
}
@end
// ViewController.m
#import "ViewController.h"
#import "MJPermenantThread.h"
@interface ViewController ()
@property (strong, nonatomic) MJPermenantThread *thread;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.thread = [[MJPermenantThread alloc] init];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.thread executeTask:^{
NSLog(@"执行任务 - %@", [NSThread currentThread]);
}];
}
- (void)dealloc
{
NSLog(@"%s", __func__);
}
@end
代码实现了线程保活的逻辑,创建了 source 并将其添加到当前线程的 runloop 的 kCFRunLoopDefaultMode 中,然后启动 runloop
/* 启动
* CFRunLoopMode mode :runloop 运行在哪个 mode 下,这里是 kCFRunLoopDefaultMode
* CFTimeInterval seconds:runloop 保持运行的时间,这里传入了一个很大的数 1.0e10
* Boolean returnAfterSourceHandled:如果设置为true,代表执行完source后就会退出当前loop
*/
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
启动之后线程会在 1.0e10 s 的时间内保活,如果想结束 runloop ,可以调用以下代码结束,从而使得线程结束。
CFRunLoopStop(CFRunLoopGetCurrent());
获取RunLoop对象
Foundation
// 获得当前线程的RunLoop对象
[NSRunLoop currentRunLoop];
// 获得主线程的RunLoop对象
[NSRunLoop mainRunLoop];
Core Foundation
// 获得当前线程的RunLoop对象
CFRunLoopGetCurrent();
// 获得主线程的RunLoop对象
CFRunLoopGetMain();
RunLoop相关的类
Core Foundation中关于RunLoop的5个类
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
typedef struct __CFRunLoop *CFRunLoopRef;
struct __CFRunLoop {
pthread_t _pthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
};
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timer;
};
#### CFRunLoopModeRef
image.png
- CFRunLoopModeRef代表RunLoop的运行模式
- 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
- RunLoop启动时只能选择其中一个Mode,作为currentMode
- 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入,不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
- 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
RunLoop 有五种运行模式,其中常见的是前两种
kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode
CFRunLoopSourceRef
Source分为两种
Source0:非基于Port的 用于用户主动触发的事件(点击button 或点击屏幕)
Source1:基于Port的 通过内核和其他线程相互发送消息(与内核相关)
CFRunLoopTimerRef
- NSTimer
- performSelector:withObject:afterDelay:
CFRunLoopObserverRef
- 用于监听RunLoop的状态
- UI刷新(BeforeWaiting)
- Autorelease pool(BeforeWaiting)
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入 Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 即将从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将推出 Loop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
添加Observer监听RunLoop的所有状态
// 创建Observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"kCFRunLoopEntry");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"kCFRunLoopBeforeTimers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"kCFRunLoopBeforeSources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"kCFRunLoopBeforeWaiting");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"kCFRunLoopAfterWaiting");
break;
case kCFRunLoopExit:
NSLog(@"kCFRunLoopExit");
break;
default:
break;
}
});
// 添加Observer到RunLoop中
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
// 释放
CFRelease(observer);
RunLoop的运行逻辑
RunLoop的运行逻辑源码分析
// 供外部调用的公开的CFRunLoopRun方法,其内部会调用CFRunLoopRunSpecific
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
// 经过精简的 CFRunLoopRunSpecific 函数代码,其内部会调用__CFRunLoopRun函数
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
// 通知Observers : 进入Loop
// __CFRunLoopDoObservers内部会调用 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
函数
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 核心的Loop逻辑
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Observers : 退出Loop
if (currentMode->_observerMask & kCFRunLoopExit ) __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
if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {
// 处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
}
// 如果有Sources1,就跳转到handle_msg标记处
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
// 通知Observers:即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
// 进入休眠,等待其他消息唤醒
__CFRunLoopSetSleeping(rl);
__CFPortSetInsert(dispatchPort, waitSet);
do {
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
} while (1);
// 醒来
__CFPortSetRemove(dispatchPort, waitSet);
__CFRunLoopUnsetSleeping(rl);
// 通知Observers:已经唤醒
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg: // 看看是谁唤醒了RunLoop,进行相应的处理
if (被Timer唤醒的) {
// 处理Timer
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
}
else if (被GCD唤醒的) {
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else { // 被Sources1唤醒的
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply);
}
// 执行Blocks
__CFRunLoopDoBlocks(rl, rlm);
// 根据之前的执行结果,来决定怎么做,为retVal赋相应的值
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}
上述源代码中,相应处理事件函数内部还会调用更底层的函数,内部调用才是真正处理事件的函数,通过上面bt打印全部堆栈信息也可以得到验证。
__CFRunLoopDoObservers 内部调用
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
__CFRunLoopDoBlocks 内部调用
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
__CFRunLoopDoSources0 内部调用
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
__CFRunLoopDoTimers 内部调用
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
GCD 调用
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
__CFRunLoopDoSource1 内部调用
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
RunLoop处理逻辑流程图
RunLoop休眠的实现原理
RunLoop休眠的实现原理RunLoop在实际开中的应用
控制线程生命周期(线程保活)
解决NSTimer在滑动时停止工作的问题
监控应用卡顿
性能优化
网友评论