RunLoop
RunLoop就是运行循环,处理app中的各种事件(比如触摸事件,定时器事件,Selector事件)
一个线程对应一个RunLoop,主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建,RunLoop在第一次获取时创建,线程结束时销毁
RunLoop的获得
- Foundation
- [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
- [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
- Core Foundation
- CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
- CFRunLoopGetMain(); // 获得主线程的RunLoop对象
RunLoop相关类
- Core Foundation中关于RunLoop的5个类
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopObserverRef
RunLoop的组成
- Mode
- Timer
- source
- Observer
Mode
Mode代表RunLoop的运行模式。每次RunLoop职能指定一个Mode运行。切换Mode职能退出Loop,再重新指定一个Mode进入。
一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer
系统默认的5个Mode
-
NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行
-
UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
-
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
-
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
-
NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode(既执行Default中的事件也执行track中的事件)
Timer
CFRunLoopTimerRef是基于时间的触发器
CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响
GCD的定时器不受RunLoop的Mode影响
- (void)timer
{
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
// 定时器只运行在NSDefaultRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作(比如textField的拖动)
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 定时器只运行在UITrackingRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
// 定时器会跑在标记为common modes的模式下
// 标记为common modes的模式:UITrackingRunLoopMode和NSDefaultRunLoopMode
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)timer2
{
// 调用了scheduledTimer返回的定时器,已经自动被添加到当前runLoop中,而且是NSDefaultRunLoopMode
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
// 修改模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)run
{
NSLog(@"----run");
}
Source
-
CFRunLoopSourceRef是事件源(输入源)
-
按照官方文档,Source的分类
- Port-Based Sources (跟其他线程交互,或通过内核发送的消息)
- Custom Input Sources (自动以输入源,几乎不用)
- Cocoa Perform Selector Sources (比如performSelecter等方法)
-
按照函数调用栈,Source的分类
- Source0:非基于Port的
- Source1:基于Port的,通过内核和其他线程通信,接收、分发系统事件
事件的传入都是先传入Source1。然后再由Source1分发至source0进行处理。所以在函数调用栈中往往只看到Source0。
Obersver
CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
- 可以监听的时间点
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 1
kCFRunLoopBeforeTimers = (1UL << 1), // 2
kCFRunLoopBeforeSources = (1UL << 2), // 4
kCFRunLoopBeforeWaiting = (1UL << 5), // 32
kCFRunLoopAfterWaiting = (1UL << 6), // 64
kCFRunLoopExit = (1UL << 7), // 128
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
- 添加Observer
- (void)observer
{
// 创建observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"----监听到RunLoop状态发生改变---%zd", activity);
});
// 添加观察者:监听RunLoop的状态
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// 释放Observer
//凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release
//比如CFRunLoopObserverCreate
//release函数:CFRelease(对象);
CFRelease(observer);
}
RunLoop处理逻辑
- 1:通知观察者run loop已经启动
- 2:通知观察者任何即将要开始的定时器
- 3:通知观察者任何即将启动的source0
- 4:启动任何准备好的source0
- 5:如果接收到source1准备好并处于等待状态,立即启动,并进入步骤9
- 6:通知观察者线程进入休眠
- 7:将线程置于休眠直到任何一下面的事件发生:
- 某一事件到达source1
- 定时器启动
- runloop设置的时间超时
- runloop被显式唤醒
- 8:通知观察者线程将被唤醒
- 9:处理未处理的事件
- 如果用户定义的定时器启动,处理定时器事件并重启runloop,进入步骤2
- 如果输入源启动,传递相应的消息
- 如果runloop被显示唤醒而且时间还未超时,重启runloop,进入步骤2
- 10:通知观察者runloop结束
网友评论