一、概念
runloop 程序在运行过程中循环的做一些事情;
二、 作用
处理下面的事件:
定时器
GCD
事件相应识别,页面刷新
网络请求
atuoReleasPool
1.程序不会马上退出,而是保持运行状态
2.处理APP中的各种事件(比如触摸事件、定时器事件等)
3.节省cpu资源,提高程序性能:该做事做事该休息休息
三.具体操作:
UIApplicationMain 创建runloop;已经main函数的runloop
伪代码如下:
int retVal = 0;
do {
//睡眠中等待消息
int message = sleep_and_wait();
// 处理消息
retVal = perccess_message(message);
} while (retVal ==0);
return 0;
四:获取runloop的api
NSRunLoop 是基于CFRunLoopRef的一层oc包装
CFRunLoopRef 是开源的:https://opensource.apple.com/tarballs/CF/
五、总结
1.每条线程都有唯一的一个与之对应的RunLoop对象;
2.RunLoop保存在一个全局的dictinary里面,线程作为key,RunLoop作为value
3.线程刚创建时并没有runloop对象,runloop会在第一次获取它时创建
4.runloop会在线程结束时销毁
5.主线程的runloop已经自动获取创建,子线程默认没有开启runloop
从底层代码分析看出来:
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
在获取runloop的如果没有runloop,会创建一个new的runloop对象,并且从 loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
这句看,是把线程作为key,Runloop作为value存在字典里面去,
在子线程开启:
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
六 、runloop底层结构分析
runloop对象的基本结构 跟runloop相关的5个类:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopsourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
struct __CFRunLoop {
=========关键的5个成员变量
pthread_t _pthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
};
struct __CFRunLoopMode {
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
};
从底层结构看出来:
1)、结构分析:
1.CFRunLoopModeRef代表runloop的运行模式;
2.一个runloop包含若干个Mode,每个Mode又包含若干个soure0/soure1/timer/ObserVer;
3.runloop启动时只能选择其中一个mode,作为CurrentMode
4.如果需要切换mode,只能退出当前Loop,再重新选择一个Mode进入;
这样的作用是将不同组的source0/source1/Timer/Observer能分隔开来,互不影响
5.如果mode里面没有任何soure0/soure1/Timer/Observer,runloop会立即退出;
注:此处的退出并不是runloop退出while循环,不会导致程序退出,而是,在while循环里面进行model的切换;
2)、mode的类型有
kCFRunLoopDefaultMode;NSDefaultRunLoopMode;APP的默认mode,通常主线程是在这个mode下运行;
UITrackingRunLoopMode; 滚动model 一帮常见的是这2种模式;界面跟踪Mode,用于scrollView追踪触摸滑动,保证页面滑动时不受其他Mode影响;
kCFRunLoopCommonModes 默认包括kCFRunLoopDefaultMode、UITrackingRunLoopMode;2种模式都兼容
// UITrackingRunLoopMode\NSDefaultRunLoopMode才是真正存在的模式
// NSRunLoopCommonModes并不是一个真正的模式,而是一个标记;
// timer在设置了common标记的模式下都可以运行;
GSEventReceiveRunLoopMode
3)、modle内部参数说明
source0
1.触摸事件处理
2.performSelector:onThread:
source1
1.基于Port的线程间通信;
2.系统事件捕捉,(先通过soure1处理,然后分发到soure0来处理);
Timers
[self performSelector:@selector(playInputClick) withObject:self afterDelay:2];
Obervers
用于监听RunLoop的状态;(休眠或者唤醒)
UI刷新(BeforeWaiting)
Autorelease Pool
七:Obervers 自己添加
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //即将进入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
方法一:
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities, YES, 0, observeRunLoopActicities, NULL);
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
CFRelease(observer);
方法二:
CFRunLoopObserverRef observer1 = 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;
casekCFRunLoopExit:
NSLog(@"kCFRunLoopExit");
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetMain(), observer1, kCFRunLoopCommonModes);
CFRelease(observer1);
// 处理block的逻辑
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
});
八 、runloop底层原理分析
1)、入口函数
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
//通知Oberservers进入loop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 具体要做的事情
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Oberserver:退出Loop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
2)、__CFRunLoopRun内部函数分析
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
//通知Oberservers进入loop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 具体要做的事情
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Oberserver:退出Loop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
__CFRunLoopRun这个函数是loop的关键处理的代码逻辑
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
// 通知observers:即将处理timer
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 通知observers:即将处理Sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//处理block;
__CFRunLoopDoBlocks(rl, rlm);
//处理soure0
if ( __CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {
//处理block;
__CFRunLoopDoBlocks(rl, rlm);
}
//判断有没有soure1
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
// 如果有soure1,就跳转到handle_msg
goto handle_msg;
}
// 通知OberVers:即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
// 等待别的消息来唤醒当前线程
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
__CFRunLoopUnsetSleeping(rl);
// 通知observe:休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:;
if (被timer唤醒) {
// 处理timer
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())
}
else if (被GCD唤醒) {
// 处理GCD
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else { //被soure1唤醒
// 处理soure1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
}
//再次处理Block
__CFRunLoopDoBlocks(rl, rlm);
// 设置返回值
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)、休眠函数分析
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
休眠是线程阻塞,cup不会给它分配资源,代码不会往下执行;当前线程休息,CUP也休息;用户态切换到内核态;
具体的实现:
mach_msg();直接sleep;内核层面的休息;
用户态 内核态
mach_msg()-------> mach_msg()
等待消息
没有消息就让当前线程休息
有消息让线程唤醒
whiel(1);这也是一种阻塞,当前线程没有休息还是在执行,CUP没有休息;
总结如图所示:
九、实际应用场景
1.控制线程生命周期(AFNetWorking)
2.解决NStimer在滑动时停止工作问题
3.性能优化,
4.监控应用卡顿