以下是我对runloop的一些基础认识的总结
1 每个线程都有一个自动创建好的runloop,但是只有主线程的runloop是默认开启的,其他子线程需要调用
NSRunLoop *runloop =[NSRunLoop currentRunLoop]; 所以Runloop 是不可主动创建的只能获取
它的启动方式一共有三种:
- Unconditionally
- With a set time limit
- In a particular mode
这三种进入方式分别对应了三种方法:
- run
- runUntilDate
- runMode:beforeDate:
run方法的本质就是无限调用 runMode:beforeDate:方法,同样地,runUntilDate:也会重复调用 runMode:beforeDate:,区别在于它超时后就不会再调用。
总结来说,runMode:beforeDate:表示的是 runloop的单次调用,另外两者则是循环调用。
它的关闭方式:
runloop 并没有可以调用的api 比如 stop之类的可以终止。所以它的关闭方式就是在开启的时候设置的 runUntilDate, runMode:beforeDate:,在某个时间结束。
所以如果你使用方法 run 去开启那你就不能手动去关闭这个runloop了,只能等到它所在的线程死掉然后它也跟着死掉。
另外推荐一对 开启关闭runloop的API
CFRunLoopRun()启动 runloop,
可以通过 CFRunLoopStop()方法结束。
注意 CFRunLoopStop() 是不能关闭 用run方法开启的runloop
因为
CFRunLoopStop()方法只会结束当前的 runMode:beforeDate:调用,而不会结束后续的调用,是因为上述说的 run 方法的本质就是无限调用 runMode:beforeDate: 方法。
以下是运用到runloop原理的地方
- NSTimer计时器完全依赖于runloop
- UIEvent事件的产生到分发给代码都是通过runloop
- Autorelease自动释放也是在runloop跑完一圈后
- NSObject(NSDelayedPerforming) performSelector,cancel
- NSObject(NSThreadPerformAddition) performSelectorOnMainThread,performSelectorOnBackgroundThread
- CA层的CADisplayLink(每画一帧会有一个回调),CATransition,CAAnimation
- dispatch_get_main_queue()
- NSURLConnection
- AFNetworking,它的delegate跟网络传输数据都是在它的runloop里面执行的
- NSPort描述通讯信道的抽象类
当然还有其它很多的地方,我就不列举了。
NSDefaultRunLoopMode
NSRunLoopCommonModes
其它模式
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode(比如绘图),通常用不到
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用 在appdelegate 打印出的当前loop 就是这个
第一种模式是默认的,NSTimer 计时器完全依赖于runloop的,如果希望界面滑动的时候不受影响那就把timer加入到另一个模式里面。
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
UITrackingRunLoopMode是runloop的滑动模式滑动的时候系统会把当前runloop从NSDefaultRunLoopMode切换到UITrackingRunLoopMode。
NSRunLoopCommonModes 其实是一个数组包含了
NSDefaultRunLoopMode
事件产生的地方,分为Source0和Source1两种
-
Source0: 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件
-
Source1: 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程
kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@
"AFNetworking"
];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
/*
通常情况下,调用者需要持有这个 NSMachPort (mach_port) 并在外部线程通过这个 port 发送消息到 loop 内;
但此处添加 port 只是为了让 RunLoop 不至于退出,并没有用于实际的发送消息
*/
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return
_networkRequestThread;
}
因为这个数据的处理是线程完成任务之后的事情,如果任务完成线程死掉这是不行的,AFNetworking 是这样保活线程的。
|