-
什么是RunLoop
- 运行循环
- 一个线程对应一个RunLoop,主线程的RunLoop默认已经启动,子线程的RunLoop需手动启动(调用run方法)
- RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop
-
用来在线程上管理事件异步到达的基础设施
,runLoop为 线程监测一个或多个事件源
-
RunLoop作用
- 保持程序的持续运行
- 处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)
- 节省CPU资源,提高程序性能:该做事时做事,该休息时休息(重要)
- 调用解耦(类似详细队列Message Queue)
-
模拟RunLoop内部实现
- 其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)
- 获得RunLoop对象
- RunLoop对象
- NSRunLoop (Foundation框架)
- CFRunLoopRef (Core Foundation框架)
- Foundation
- RunLoop对象
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
+ Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
- RunLoop结构
- CFRunLoopRef对应RunLoop对象
- CFRunLoopModeRef代表RunLoop的
运行模式, 系统默认注册了5个Mode
- NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
- NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
- CFRunLoopTimerRef是基于时间的触发器
- CFRunLoopTimerRef基本上说的就是NSTimer、CADisplayLink等,它受RunLoop的Mode影响
- CFRunLoopSourceRef是事件源(输入源)
- Source是RunLoop的数据源抽象类(protocol)
- Sourece0:处理App内部事件、App自己负责管理(触发),如UIEvent
- Sourece1:由RunLoop和内核管理,Mach port驱动,如CFMachPort等
- Source是RunLoop的数据源抽象类(protocol)
- CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变,框架中很多机制都都RunLoopObserver触发,如CAAnimation
// 1.创建Observer
// 第一个参数:用于分配该observer对象的内存
// 第二个参数:用以设置该observer所要关注的的事件
// 第三个参数:用于标识该observer是在第一次进入run loop时执行, 还是每次进入run loop处理时均执行
// 第四个参数:用于设置该observer的优先级
// 第五个参数: observer监听到事件时的回调block
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch(activity)
{
case kCFRunLoopEntry:
NSLog(@"即将进入loop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理sources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"刚从休眠中唤醒");
break;
case kCFRunLoopExit:
NSLog(@"即将退出loop");
break;
default:
break;
}
});
// 2.添加监听
/*
第一个参数: 给哪个RunLoop添加监听
第二个参数: 需要添加的Observer对象
第三个参数: 在哪种模式下监听
*/
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
// 3,释放observer
CFRelease(observer);
- RunLoop处理逻辑(事件队列).
每次运行 run loop,你线程的 run loop 对会自动处理之前未处理的消息,并通
知相关的观察者。具体的顺序如下:- 1.通知观察者run loop 已经启动,即将进入runloop
- 2.通知观察者任何即将要开始的定时器
- 3.通知观察者任何即将启动的
非基于端口
的源 - 4.启动任何准备好的
非基于端口
的源 - 5.如果
基于端口
的源准备好并处于等待状态,立即启动;并进入步骤9 - 6.通知观察者线程进入休眠
- 7.将线程置于休眠直到任一下面的事件发生:
- 7.1 某一事件到达
基于端口
的源、定时器启动、run loop 被显式唤醒
- 7.1 某一事件到达
- 8.通知观察者线程将被唤醒
- 9.处理未处理的事件
- 9.1 如果用户定义的定时器启动,处理定时器事件并重启 run loop。进入步骤 2
- 9.2 如果输入源启动,传递相应的消息
- 9.3 如果 run loop 被显式唤醒而且时间还没超时,重启 run loop。进入步骤 2
- 10.通知观察者 run loop 结束
.png)
- RunLoopRunLoop应用
- NSTimer
- 只能在指定的model下运行
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
- ImageView显示
- 只能在指定的model下设置图片
- PerformSelector
- 只能在指定的model下调用
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageNamed:@"lnj"] waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];
- 常驻线程
- 必须调用run才会执行死循环
- NSRunLoop的model中必须有source/timer,死循环才不会退出
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runloop run]
- 自动释放池
activities = 0x1 = 1
1: 即将进入RunLoop : 创建一个自动释放池
activities = 0xa0 = 160 = 128 + 32
32:即将休眠 : 释放上一次的自动释放池, 创建一个新的自动释放池
128:即将退出RunLoop : 释放自动释放池
1.NSRunLoop的实现机制,及在多线程中如何使用
NSRunLoop是IOS消息机制的处理模式
>1.NSRunLoop的主要作用:控制NSRunLoop里面线程的执行和休眠,在有事情做的时候使当前NSRunLoop控制的线程工作,没有事情做让当前NSRunLoop的控制的线程休眠。
>2.NSRunLoop 就是一直在循环检测,从线程start到线程end,检测inputsource(如点击,双击等操作)异步事件,检测timesource同步事件,检测到输入源会执行处理函数,首先会产生通知,corefunction向线程添加runloop observers来监听事件,意在监听事件发生时来做处理。
>3.runloopmode是一个集合,包括监听:事件源,定时器,以及需通知的runloop observers
>1. 只有在为你的程序创建次线程的时候,才需要运行run loop。对于程序的主线程而言,run loop是关键部分。Cocoa提供了运行主线程run loop的代码同时也会自动运行run loop。IOS程序UIApplication中的run方法在程序正常启动的时候就会启动run loop。如果你使用xcode提供的模板创建的程序,那你永远不需要自己去启动run loop
>2. 在多线程中,你需要判断是否需要run loop。如果需要run loop,那么你要负责配置run loop并启动。你不需要在任何情况下都去启动run loop。比如,你使用线程去处理一个预先定义好的耗时极长的任务时,你就可以毋需启动run loop。Run loop只在你要和线程有交互时才需要
面试题:请谈一谈runloop
一条线程对应一个runloop,而主线程只要一启动会默认绑定一个runloop.那系统内部我查看过源码,他是通过字典的形式把我的线程和我的这个runloop绑定在一起.子线程的runloop默认情况下是没有的,如果想使用子线程的runloop,只需要调用currentrunloop就可以,我们可以把runloop理解为一个懒加载的形式,只有用到的时候才会创建.只要调用currentrunloop,他就会根据字典key去内存中取,如果没有,就会创建一个新的runloop,和我们当前的子线程绑定在一起,并且保存到字典当中,runloop和线程是一个一对一的关系.
每个runloop内有很多mode模式,每一个model里面又有很多的source、timer、observer,runloop只能执行一个模式,如果执行A模式,那么其他模式下的source、timer、observer,无效,只有A模式有效.(为什么要这样做?)防止多个模式下的source、timer、observer,紊乱,不好控制逻辑
timer 的执行流程?通过timer来监听执行的流程1、进入runloop,首先会处理系统的事件,处理完系统的source,timer后runloop会进入休眠状态.
只要我们触发一些事件以后,runloop会醒来,就会处理timer,source
runloop什么时候死掉?
1、创建timer时,会给timer创建一个默认的生命周期,我们也可以自定义生命周期 2、线程挂掉
网友评论