RunLoop介绍
- Run loop 本身听起来就和它的名字很像。它是一个循环,你的线程进入并使用它来运行响应输入事件的事件处理程序。你的代码要提供实现循环部分的控制语句,换言之就是要有 while 或 for 循环语句来驱动 run loop。在你的循环中,使用 run loopobject 来运行事件处理代码,它响应接收到的事件并启动已经安装的处理程序。节省CPU资源,提高程序性能:该做事时做事,该休息时休息
RunLoop对象
iOS中有2套API来访问和使用RunLoop
Foundation ->NSRunLoop
Core Foundation ->CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)RunLoop与线程
1.一条线程对应一个RunLoop
2.RunLoop在第一次获取时创建,在线程结束时销毁
3.主线程的RunLoop默认已经创建好了, 而子线程的需要我们自己手动创建
4.一个NSRunLoop/CFRunLoopRef, 就代表一个RunLoop对象
5.只要线程结束了, 那么与之对应的RunLoop对象也会被释放
每条线程都有唯一的一个与之对应的RunLoop对象
6.如何获取主线程对应的RunLoop对象,
mainRunLoop/CFRunLoopGetMain
7.如何获取当前线程对应的RunLoop对象,Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
RunLoop相关类
- Core Foundation中关于RunLoop的5个类
1.CFRunLoopRef
2.CFRunLoopModeRef
3.CFRunLoopSourceRef
4.CFRunLoopTimerRef
5.CFRunLoopObserverRef
- NSRunLoopMode中的类
- CFRunLoopModeRef代表RunLoop的运行模式,一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer,每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode,如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入,这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响
- 系统默认注册了5个Mode:
1.NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行
2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
3.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
4.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
5.NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode - NSRunLoopMode的应用
//1.创建一个NSTimer
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
// 2.将NSTimer添加到RunLoop中, 并且告诉系统, 当前Tiemr只有在RunLoop的默认模式下才有效
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 2.将NSTimer添加到RunLoop中, 并且告诉系统, 当前Tiemr只有在Tracking的默认模式下才有效
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
// 2.将NSTimer添加到RunLoop中, 并且告诉系统, 在所有被"标记"common的模式都可以运行,
UITrackingRunLoopMode和kCFRunLoopDefaultMode都被标记为了common模式, 所以只需要
将timer的模式设置为forMode:NSRunLoopCommonModes,就可以在默认模式和追踪模式都能够
运行
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// 注意: 如果是通过scheduledTimerWithTimeInterval创建的NSTimer, 默认就会添加到
RunLoop得DefaultMode中 , 所以它会自动运行
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
// 虽然默认已经添加到DefaultMode中, 但是我们也可以自己修改它的模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
- (void)show
{
NSLog(@"%s", __func__);
}
- CFRunLoopObserverRef
CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
// 创建Observer
/*
第1个参数: 指定如何给observer分配存储空间
第2个参数: 需要监听的状态类型/ kCFRunLoopAllActivities监听所有状态
第3个参数: 是否每次都需要监听
第4个参数: 优先级
第5个参数: 监听到状态改变之后的回调
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即将进入runloop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timer");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理source");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入睡眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"刚从睡眠中唤醒");
break;
case kCFRunLoopExit:
NSLog(@"即将退出");
break;
default:
break;
}
});
// 给主线程的RunLoop添加一个观察者
/*
第1个参数: 需要给哪个RunLoop添加观察者
第2个参数: 需要添加的Observer对象
第3个参数: 在哪种模式下可以可以监听
*/
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
// 释放对象
凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release
比如CFRunLoopObserverCreate
release函数:CFRelease(对象);
CFRelease(observer);
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
- (void)show{
}
CFRunLoopSourceRef
CFRunLoopSourceRef是事件源(输入源)按照官方文档,Source的分类
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources按照函数调用栈,Source的分类
Source0:非基于Port的, 用于用户主动触发事件
Source1:基于Port的,通过内核和其他线程相互发送消息CFRunLoopTimerRef
CFRunLoopTimerRef是基于时间的触发器
CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响
GCD的定时器不受RunLoop的Mode影响
RunLoop应用场景
- (void)viewDidLoad
{
//自动释放池什么时候创建和释放
// 1.第一次创建, 是在runloop进入的时候创建 对应的状态 = kCFRunLoopEntry
//2.最后一次释放, 是在runloop退出的时候 对应的装 = kCFRunLoopExit
//3.其它创建和释放
//每次睡觉的时候都会释放前自动释放池, 然后再创建一个新的
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(show) object:nil];
self.thread = thread;
[thread start];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"%s", __func__);
/*
// 1.在指定模式下进行特定的操作
[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"abc"] afterDelay:2.0 inModes:@[UITrackingRunLoopMode]];
*/
// 默认清空下一个线程只能使用一次, 也就是说只能执行一个操作, 执行完毕之后就不能使用了
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)show{
NSLog(@"%s", __func__);
// 1.子线程的NSRunLoop需要手动创建
// 2.子线程的NSRunLoop需要手动开启
// 3.如果子线程的NSRunLoop没有设置source or timer, 那么子线程的NSRunLoop会立刻关闭
//相当于添加了一个基于端口的source
// [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
// NSTimer *timer = [NSTimer timerWithTimeInterval:5.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// [[NSRunLoop currentRunLoop] run];
// 注意点: NSRunLoop只会检查有没有source和timer, 没有就关闭, 不会检查observer
CFRunLoopObserverRef observer =
CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// 释放对象
CFRelease(observer);
[[NSRunLoop currentRunLoop] run];
NSLog(@"end -----");
}
- (void)test
{
NSLog(@"%s %@", __func__, [NSThread currentThread]);
}