1 什么是Runloop
从字面意思看,Runloop的意思就是
运行循环,跑圈
Runloop基本作用:
- 1.保持程序的持续运行
- 2.处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
- 3.节省CPU资源,提高程序性能:该做事时做事,该休息时休息
如果没有Runloop
int main(int argc, char * argv[]) {
NSLog(@"execute main function");//程序开始
return 0;//程序结束
}
Runloop与线程
每条线程都有唯一的一个与之对应的RunLoop对象
主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
RunLoop在第一次获取时创建,在线程结束时销毁
2 Runloop对象
iOS中有2套API来访问和使用RunLoop
- Foundation
- NSRunLoop
- Core Foundation
- CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表着RunLoop对象
- NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)
- Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
- Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
3 Runloop相关的类
Core Foundation中关于RunLoop的5个类
- CFRunLoopRef
- CFRunLoopModeRef 【Runloop的运行模式】
- CFRunLoopSourceRef 【Runloop要处理的事件源】
- CFRunLoopTimerRef 【Timer事件】
- CFRunLoopObserverRef 【Runloop的观察者(监听者)】
CFRunLoopRef
- CFRunLoopModeRef代表RunLoop的运行模式
一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer - 每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode
- 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
- 这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响
系统默认注册了5个Mode:
- kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
- 典型事例就是拖动scrollview时,NSTimer不工作,其原因就是,拖动时runloop切换到界面追踪模式,此时其他模式的事件(例如定时器事件)就无法继续。
- UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
- GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
- kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
CFRunLoopSourceRef
CFRunLoopSourceRef是事件源(输入源)
- 以前的分法
- Port-Based Sources
- Custom Input Sources
- Cocoa Perform Selector Sources
- 现在的分法
- Source0:非基于Port的
- Source1:基于Port的
CFRunLoopTimerRef
- CFRunLoopTimerRef是基于时间的触发器
- 基本上说的就是NSTimer
CFRunLoopObserverRef
- CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
- 可以监听的时间点有以下几个
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //即将进入Runloop
kCFRunLoopBeforeTimers = (1UL << 1), //即将处理NSTimer
kCFRunLoopBeforeSources = (1UL << 2), //即将处理Sources
kCFRunLoopBeforeWaiting = (1UL << 5), //即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), //刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), //即将退出runloop
kCFRunLoopAllActivities = 0x0FFFFFFFU //所有状态改变
};
4 重点-Runloop运行逻辑
5 重点-Runloop应用
1)NSTimer
2)ImageView显示:控制方法在特定的模式下可用
3)PerformSelector
4)常驻线程:在子线程中开启一个runloop
5)自动释放池的释放时机:
第一次创建:进入runloop的时候
最后一次释放:runloop退出的时候
其它创建和释放:当runloop即将休眠的时候会把之前的自动释放池释放,然后重新创建一个新的释放池
5 重点-Runloop应用
- 监听Runloop,可以在按钮响应方法之前做些事情
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self observer];
}
-(void)observer
{
//1.创建监听者
/*
第一个参数:怎么分配存储空间
第二个参数:要监听的状态 kCFRunLoopAllActivities 所有的状态
第三个参数:时候持续监听
第四个参数:优先级 总是传0
第五个参数:当状态改变时候的回调
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
/*
kCFRunLoopEntry = (1UL << 0), 即将进入runloop
kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件
kCFRunLoopBeforeSources = (1UL << 2),即将处理source事件
kCFRunLoopBeforeWaiting = (1UL << 5),即将进入睡眠
kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒
kCFRunLoopExit = (1UL << 7), runloop退出
kCFRunLoopAllActivities = 0x0FFFFFFFU
*/
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(@"runloop退出");
break;
default:
break;
}
});
/*
第一个参数:要监听哪个runloop
第二个参数:观察者
第三个参数:运行模式
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer, kCFRunLoopDefaultMode);
//NSDefaultRunLoopMode == kCFRunLoopDefaultMode
//NSRunLoopCommonModes == kCFRunLoopCommonModes
}
- (IBAction)RunloopObserver:(id)sender {
NSLog(@"处理点击事件:%s",__func__);
}
效果图
- 创建一个常驻线程
- (IBAction)createThread:(id)sender {
//1.创建线程
self.thread = [[NSThread alloc]initWithTarget:self selector:@selector(task) object:nil];
[self.thread start];
}
-(void)task
{
NSLog(@"task---%@",[NSThread currentThread]);
// while (1) {
// NSLog(@"task1---%@",[NSThread currentThread]);
// }
//解决方法:开runloop
//1.获得子线程对应的runloop
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
//保证runloop不退出
//NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
//[runloop addTimer:timer forMode:NSDefaultRunLoopMode];
[runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
//2.默认是没有开启
[runloop run];//开启
// [runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];//开启Runloop,到特定时间结束
NSLog(@"---end----");
}
- (IBAction)doOthertask:(id)sender {
//[self.thread start];
[self performSelector:@selector(OtheTask) onThread:self.thread withObject:nil waitUntilDone:YES];
}
-(void)OtheTask
{
NSLog(@"OtheTask---%@",[NSThread currentThread]);
}
-(void)run
{
NSLog(@"%s",__func__);
}
//Runloop中自动释放池的创建和释放
//第一次创建:启动runloop
//最后一次销毁:runloop退出的时候
//其他时候的创建和销毁:当runloop即将睡眠的时候销毁之前的释放池,重新创建一个新的
效果图
Runloop源码分析
资料