文章目录
1. 简介
iOS 中负责程序运行循环,在程序运行过程中循环做一些事情;保持程序的运行,程序运行时会在main函数中创建一个runloop,负责主线程的持续运行;及处理app中的各种事件的响应,例如:NSTimer,UITouch等;在程序需要处理事件时候runloop就唤起线程,当不许处理事件的时候线程进入休眠状态,这样可以节省线程资源。
在OC中每一个线程都有唯一的一个Runloop对象;Runloop对象保存在一个全局的dictionary里,线程作为键Runloop作为值;线程刚创建的时候是没有runloop,当第一次获取的时候创建;mian线程的Runloop程序运行时已经获取了。
2. Runloop 对象获取
源码可以在苹果开源网站下载,
2.1 get方法
在源码里找到CFRunLoop.c
文件,在该文件里搜索 CFRunloopGetCurrent()方法;该方法里调用了_CFRunLoopGet0(pthread_self());
,下面看下该方法实现:
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {//如果不存在,则创建一个保存Runloop对象的字典,
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());//创建线程的runloop
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;
}
2.2 runLoop 相关类及执行状态
常用相关的类主要有5个:CFRunLoopRef、CFRunLoopModelRef、CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObseverRef;
CFRunLoop在公开的源码里的实现是:
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;//当前的模式
CFMutableSetRef _modes;//所有的运行模式
};
struct __CFRunLoopMode {
CFRuntimeBase _base;
CFStringRef _name;
CFMutableSetRef _sources0;//触摸事件,performSelector:onThread:withobject:
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
};
从上述源码中,可以看出一个RunLooop可以包含多个mode,每一个mode又包含多个_sources0、_sources1、_observers、_timers;当前是那个mode取决于_currentMode
,同时也只能存在一种模式可以作为_currentMode,当需要切换到另外一种模式的时候会退出当前的mode;常用的有两种mode:kCFRunLoopDefaultMode
主线程一般都是运行在该mode下、UITrackingRunLoopMode
界面跟踪mode,界面滑动触摸,保证界面滑动时候不受其他mode的影响;
看一下触摸事件的函数调用堆栈:
接下来通过一段代码验证一下模式的切换:
CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:{
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopEntry %@",mode);
CFRelease(mode);
}
break;
case kCFRunLoopBeforeTimers: {
break;
}
case kCFRunLoopBeforeSources: {
break;
}
case kCFRunLoopBeforeWaiting: {
break;
}
case kCFRunLoopAfterWaiting: {
break;
}
case kCFRunLoopExit: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopExit %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopAllActivities: {
break;
}
}
});
// CFRunLoopObserverRef obRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
//
// }, NULL);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
CFRelease(obRef);
滑动view后的输出:
3. RunLoop 运行逻辑
以下部分内容来自iOS底层原理总结 - RunLoop
3.1 RunLoop 调用过程
RunLoop 在CoreFoundation框架中可以添加消息监听
CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:{
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopEntry 进入 %@",mode);
CFRelease(mode);
}
break;
case kCFRunLoopBeforeTimers: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopBeforeTimers 处理timer %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopBeforeSources: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopBeforeSources 处理source %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopBeforeWaiting: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopBeforeWaiting 开始进入休息 %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopAfterWaiting: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopAfterWaiting 已经恢复 %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopExit: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopExit 退出了 %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopAllActivities: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopAllActivities %@",mode);
CFRelease(mode);
break;
}
}
});
// CFRunLoopObserverRef obRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
//
// }, NULL);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
CFRelease(obRef);
控制台输出,部分代码精简:
2019-09-25 09:06:58.969802+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 处理timer kCFRunLoopDefaultMode
2019-09-25 09:06:58.970439+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 处理source kCFRunLoopDefaultMode
2019-09-25 09:06:58.970643+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 处理timer kCFRunLoopDefaultMode
2019-09-25 09:06:58.970760+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 处理source kCFRunLoopDefaultMode
2019-09-25 09:06:58.970909+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 处理timer kCFRunLoopDefaultMode
2019-09-25 09:09:00.007058+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 开始进入休息 kCFRunLoopDefaultMode
2019-09-25 09:10:00.003054+0800 testRunLoop[5136:43000] kCFRunLoopAfterWaiting 已经恢复 kCFRunLoopDefaultMode
2019-09-25 09:10:00.006223+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 处理timer kCFRunLoopDefaultMode
2019-09-25 09:10:00.006520+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 处理source kCFRunLoopDefaultMode
2019-09-25 09:10:00.006804+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 开始进入休息 kCFRunLoopDefaultMode
2019-09-25 09:11:00.002788+0800 testRunLoop[5136:43000] kCFRunLoopAfterWaiting 已经恢复 kCFRunLoopDefaultMode
2019-09-25 09:11:00.005685+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 处理timer kCFRunLoopDefaultMode
2019-09-25 09:11:00.005937+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 处理source kCFRunLoopDefaultMode
2019-09-25 09:11:00.006132+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 开始进入休息 kCFRunLoopDefaultMode
3.2 源码简介
// 共外部调用的公开的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;
}
3.3 对timer的影响
static int timerCount = 0;
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
// NSLog(@"timerCount = %d",self->_timerCount++);
NSLog(@"timerCount = %d",timerCount++);
}];
//NSDefaultRunLoopMode kCFRunLoopCommonModes
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
从控制台的时间戳里可以看出,屏幕滑动会对timer的影响;将NSDefaultRunLoopMode
换成kCFRunLoopCommonModes
屏幕的滑动就不会对timer产生影响;
4 子线程中对NSTimer的影响
4.1 子线程如何使用NSTimer,通过上面的分析知道:主线程runLoop成程序启动后会运行,而子线程的runLoop不主动调用是没有的,而且即使主动调用子线程的runloop是默认不运行的,因此子线程的timer运行,一般我们按照如下方式处理:
#import "TestTimer.h"
@interface TestTimer ()
@property(nonatomic,strong)NSTimer *timer;
@property(nonatomic,strong)dispatch_queue_t que;
@end
@implementation TestTimer
-(void)run{
static int timerCount = 0;
self.que = dispatch_queue_create("ddddd", DISPATCH_QUEUE_CONCURRENT);
__weak typeof(self) weakSelf = self;
dispatch_async(_que, ^{
__weak typeof(weakSelf) strongSelf = weakSelf;
CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:{
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopEntry 进入 %@",mode);
CFRelease(mode);
}
break;
case kCFRunLoopBeforeTimers: {
// CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
// NSLog(@"kCFRunLoopBeforeTimers 处理timer %@",mode);
// CFRelease(mode);
break;
}
case kCFRunLoopBeforeSources: {
// CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
// NSLog(@"kCFRunLoopBeforeSources 处理source %@",mode);
// CFRelease(mode);
break;
}
case kCFRunLoopBeforeWaiting: {
// CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
// NSLog(@"kCFRunLoopBeforeWaiting 开始进入休息 %@",mode);
// CFRelease(mode);
break;
}
case kCFRunLoopAfterWaiting: {
// CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
// NSLog(@"kCFRunLoopAfterWaiting 已经恢复 %@",mode);
// CFRelease(mode);
break;
}
case kCFRunLoopExit: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopExit 退出了 %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopAllActivities: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopAllActivities %@",mode);
CFRelease(mode);
break;
}
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
CFRelease(obRef);
strongSelf.timer = [NSTimer timerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
// NSLog(@"timerCount = %d",self->_timerCount++);
NSLog(@"timerCount = %d",timerCount++);
}];
//NSDefaultRunLoopMode UITrackingRunLoopMode NSRunLoopCommonModes
[[NSRunLoop currentRunLoop] addTimer:strongSelf.timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.timer invalidate];
});
}
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
运行后输出:
2019-09-25 10:24:05.161121+0800 testRunLoop[19144:159757] kCFRunLoopEntry 进入 kCFRunLoopDefaultMode
2019-09-25 10:24:06.162717+0800 testRunLoop[19144:159757] timerCount = 0
//...省略
2019-09-25 10:24:14.162738+0800 testRunLoop[19144:159757] timerCount = 8
2019-09-25 10:24:15.161226+0800 testRunLoop[19144:159625] -[TestTimer dealloc]
从上面的运行结果发现,runloop在定时器结束后并没有退出循环,如果想在结束后结束子线程循环则需要调用:[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
2019-09-25 10:28:39.161729+0800 testRunLoop[19809:166720] kCFRunLoopEntry 进入 kCFRunLoopDefaultMode
2019-09-25 10:28:40.165492+0800 testRunLoop[19809:166720] timerCount = 0
//...省略
2019-09-25 10:28:49.162015+0800 testRunLoop[19809:166720] timerCount = 9
2019-09-25 10:28:49.162109+0800 testRunLoop[19809:166667] -[TestTimer dealloc]
2019-09-25 10:28:49.162376+0800 testRunLoop[19809:166720] kCFRunLoopExit 退出了 kCFRunLoopDefaultMode
4. 线程保活
注意:NSRunloop的run方法是无法停止的,一旦掉用该方法就等于开启一个永远不会销毁的线程;及即使调用
CFRunloopStop()
也只会是停止一次
的run;及调用run会无限
开启run方法;
看一下实现:
#import "LYMPermemantThread.h"
@interface LYMThread : NSThread
@end
@implementation LYMThread
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
@interface LYMPermemantThread ()
@property(nonatomic,strong)LYMThread *innerThred;
@property(nonatomic,assign,getter=isStopped)BOOL stopped;
//@property(nonatomic,copy)void(^innerHandler)(void);
@end
@implementation LYMPermemantThread
- (instancetype)init{
if (self = [super init]) {
self.stopped = NO;
__weak typeof(self) weakSelf = self;
self.innerThred = [[LYMThread alloc]initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStopped) {//一次循环后会退出,这里为防止退出,加入循环在需要退出的时候退出线程
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
[self.innerThred start];
}
return self;
}
- (void)run{
// if (!_innerThred) {
// return;
// }
// [self.innerThred start];
}
- (void)execTaskWithHandler:(void(^)(void))handler{
if (!_innerThred || !handler) {
return;
}
[self performSelector:@selector(__execBcock:) onThread:self.innerThred withObject:handler waitUntilDone:NO];
}
- (void)stop{
if (!_innerThred) {
return;
}
[self performSelector:@selector(__stop) onThread:self.innerThred withObject:nil waitUntilDone:YES];
}
- (void) __stop{
self.stopped = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.innerThred = nil;
}
- (void)__execBcock:(void(^)(void))handler{
if (handler) {
handler();
}
}
-(void)dealloc{
NSLog(@"%s",__func__);
[self stop];
}
@end
使用CF框架实现:
- (instancetype)init{
if (self = [super init]) {
self.stopped = NO;
__weak typeof(self) weakSelf = self;
self.innerThred = [[LYMThread alloc]initWithBlock:^{
// [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//
// while (weakSelf && !weakSelf.isStopped) {
// [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
// }
CFRunLoopSourceContext cotext = {0};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &cotext);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);//false 标识source结束后不退出runloop
}];
[self.innerThred start];
}
return self;
}