1.什么是runloop?
RunLoop是通过内部维护的事件循环
来对事件/消息进行管理
的一个对象。
事件循环
-
没有消息需要处理时,休眠以避免资源占用
-
有消息处理时,立刻被唤醒
main函数是为什么可以一直保证运行状态,而不退出 ?
在main函数当中,所调用的UIApplicationMain()内部会启动主线程的runloop,而runloop又是对事件循环的一种维护机制,可以做到在有事做的时候做事,没有事情做的时候,会通过从用户态到内核态的切换,从而避免资源的占用,让当前线程处于一种休眠状态。
2.数据结构
NSRunLoop是CFRunLoop的封装,提供了面向对象的API.
- CFRunLoop
- CGRunLoopMode
- Source/Timer/Observer
CFRunLoop
CFRunLoopMode
CFRunLoopSource
- source0
需要手动唤醒线程 - source1
具备唤醒线程的能力
CFRunLoopTimer
基于事件的定时器
CFRunLoopObserver
观测时间点
- kCFRunLoopEntry
- kCFRunLoopBeforeTimers
- kCFRunLoopBeforeSources
- kCFRunLoopBeforeWaiting
- kCFRunLoopAfterWaiting
- kCFRunLoopExit
各个数据结构之间的关系
RunLoop的Mode
CommonMode的特殊性
NSRunLoopCommonModes
- CommonMode不是实际存在的一种Mode。
- 是同步Source/Timer/Observer到多个Mode中的一种技术解决方案
3.事件循环的实现机制
void CFRunLoopRun()
4.NSRunLoop与NSTimer
滑动TableView的时候我们的定时器还会生效吗?
解决方案
void CFRunLoopAddTimer(runLoop, timer,commonMode)
5.RunLoop与多线程
线程和RunLoop一一对应的。
自己创建的线程默认是没有RunLoop的
(1)怎样实现一个常驻线程?
a.为当前线程开启一个RunLoop;
[NSRunLoop currentRunLoop]或者CFRunLoopGetCurrent()
b.向该RunLoop中添加一个Port/Source等维护RunLoop的事件循环;
c.启动该RunLoop。
#import "MCObject.h"
@implementation MCObject
static NSThread *thread = nil;
// 标记是否要继续事件循环
static BOOL runAlways = YES;
+ (NSThread *)threadForDispatch{
if (thread == nil) {
@synchronized(self){
if (thread == nil) {
//线程的创建
thread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequest) object:nil];
[thread setName:@"com.imooc.thread"];
//启动
[thread start];
}
}
}
return thread;
}
- (void)runRequest{
//创建一个source
CFRunLoopSourceContext context = {0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
//创建RunLoop,同时向RunLoop的DefaultMode下面添加Source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
//如果可以运行
while (runAlways) {
@autoreleasepool {
//令当初RunLoop运行在DefaultMode下面
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10,true);
}
}
//某一时机 静态变量 runAlways = NO时,可以保证跳出RunLoop,线程退出
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}
@end
6.问题总结
(1)什么是RunLoop,它是怎样做到有事做事,没事休息的?
RunLoop实际是一个事件循环,用于处理事件和消息以及对它们进行管理。由于在调用CFRunLoopRun之后会调用系统的mach_msg()函数,同时发生了从用户态向核心态的切换,然后当前线程处于休眠状态,所以可以做到有事做时,做事没事做时休息。
(2)RunLoop与线程是怎样的关系?
a.runloop与线程是一一对应的关系
b.一个线程默认是没有runloop的,需要手动创建。
(3)实现一个常驻线程
我们可以通过三个步骤,第一个步骤呢,创建一个线程对应的runloop,之后向这个runloop中添加source、timer、observer、port等内容,第三步调用CFRunLoop的一个run方法就可以实现一个常驻线程。在具体使用的过程中,需要注意,运行的模式和资源添加的模式必须是同一个,否则应为外部使用了while循环,就导致了一个死循环
(4)怎样保证子线程数据回来更新UI的时候不打断用户的滑动操作?
在用户进行滑动过程中,当前的runloop运行在UITrackingRunLoopMode,我们对网络请求一般放在子线程中进行的,而子线程返回给主线程的数据呢,我们要抛给主线程,用来更新UI,这个时候我们可以通过把子线程抛会给主线程更新UI的逻辑包装起来,给它提交到主线程的default模式下面,这样的话,抛回来的任务,当前用户正在滑动过程中处于UITrackingRunLoopMode模式下,我们分派到defalut的模式下的任务就不会执行,而当我们手停止滑动操作之后,当前线程它的mode切换到了,default模式下,这个时候处理子线程上抛给主线程的任务,对它进行处理,这样就不会打断用户的滑动操作。