关于runloop网上很多资料我都稍微看了下,写得很详细,不可谓不好,只是对接触ios时间不长的开发者来说,可能有点难以理解,所以我想写的是一篇尽量简洁易懂的介绍。
面试的时候经常听到面试官说,能简单介绍一下runloop吗 ? 然后就没有然后了.......runloop其实我们每个项目或者说几乎每个model都有用到,只是我们自己没有真正的了解过而已,因此给初学者的感觉就是我从来没用过runloop,我也不知道它有什么用,我只知道听着很厉害的样子。
那么我们首先来看一段代码,runloop在你代码中的实际应用,而不是抽象概念。
如上的代码运行,当我们滚动scrV的时候,定时器执行的打印方法是否能正常打印?
当我们把注释给打开,定时器执行的打印方法是否能正常打印?
至于为什么?客官别着急,好戏在后头。
1: runloop是什么?
runloop就是一个运行循环,用来响应事件(同步/异步)的循环。
runloop分为两个状态:休眠,唤醒
runloop作用:用来保持程序持续运行
runloop生命周期:主线程的runloop随着程序运行已经创建,子线程需要手动创建。
Cocoa中的NSRunLoop类并不是线程安全的,即:不要在一个线程中去操作另外一个线程的runloop。但是CoreFundation中的不透明类CFRunLoopRef是线程安全的。
创建语法:
[NSRunLoop currentRunLoop];
2: runloop的构成
3: CFRunLoopMode的构造
struct __CFRunLoopMode {
CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
CFMutableSetRef _sources0; // Set
CFMutableSetRef _sources1; // Set
CFMutableArrayRef _observers; // Array
CFMutableArrayRef _timers; // Array
...
};
name分为5种类型,苹果公开的只有1,2,4.
1. kCFRunLoopDefaultMode: 默认类型。
2. UITrackingRunLoopMode: 追踪类型,Scrollview滑动的时候会跳入次模式下运行。
3. UIInitializationRunLoopMode: 启动程序后的过渡类型,启动完成后就不再使用。
4. kCFRunLoopCommonModes:可以同时包含几种类型。
5. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode。
4: CFRunLoopSource的构造
CFRunLoopSource:数据源的抽象类,包含source0和source1。
source0:处理内部事件,手动唤醒runloop。自定义输入源如方法:
performSelector:onThread:withObject:waitUntilDone:modes:
source1: 由mach port(内核)驱动,主动唤醒runloop。通过NSPort可以创建端口对象。
5: CFRunLoopTimer(定时源)
例如最上面的NSTimer,
这里需要注意,假如有一个定时器(NSTimer)是无限重复的,每间隔t秒执行一个方法;如果错过了某个触发时间点t,它会在下一次触发时间点t+1执行此方法,忽略之前的t应该执行一次方法。
6: CFRunLoopObserve(观察者)
runloop观察者是在runloop本身运行的特定时候触发
作用:每个 Observer 都包含了一个回调,当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
};
7: runloop的工程流程(图是从其他网站直接拷贝过来的)
8: runloop的创建场景
其实讲了这么多,小伙伴们最关心的东西是到底runloop有什么用?我们时候才需要创建它?当然主线程中已经有了不需要,那么其他线程什么时候需要创建?我们先看一段代码
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelectorInBackground:@selector(touch) withObject:nil];
}
- (void)touch{
//1 创建一个定时器
// [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(aa) userInfo:nil repeats:1];
//2 子线程三秒之后执行一个方法
[self performSelector:@selector(aa) withObject:nil afterDelay:3];
//创建并且启动runloop
// [[NSRunLoop currentRunLoop] run];
}
- (void)aa{
NSLog(@"1");
}
上面的代码1,2展示了如果不添加并且启动 runloop会出现的状况。
如下为需要添加runloop的情景:
1 需要使用Port或者自定义Input Source与其他线程进行通讯。
2 需要在线程中使用Timer。
3 需要在线程上使用performSelector*****等延迟执行方法。
4 需要让线程执行周期性的工作。