runloop是线程相关底层基础的一部分, 作用是接受循环事件和安排线程工作, 让线程有任务的时候工作, 而没有任务的时候处于休眠状态
runloop作用
- 保持程序持续运行
- 处理app中的各种事件
- 节省CPU资源, 提高程序性能
runloop和线程关系
- 每条线程都有唯一的Runloop对象
- 主线程的Runloop对象自动创建, 子线程的runloop需要主动创建
- runloop在第一次获取时创建, 在线程结束时销毁
创建runloop
- 通过[NSRunloop currentRunLoop]创建一个线程的runloop
原因】:currentRunLoop 本身是懒加载的,当第一次调用currentRunLoop 方法获得该子线程对应的 Runloop 的时候,它会先去判断(去字典中查找)这个线程的Runloop 是否存在,如果不存在就会自己创建并且返回,如果存在直接返回。
获取runloop
// Foundation框架
NSRunLoop *mainRunloop = [NSRunLoop mainRunLoop]; // 获得主线程对应的 runloop对象
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop]; // 获得当前线程对应的runloop对象
// Core Foundation框架
CFRunLoopRef maiRunloop = CFRunLoopGetMain(); // 获得主线程对应的 runloop对象
CFRunLoopRef maiRunloop = CFRunLoopGetCurrent(); // 获得当前线程对应的runloop对象
Runloop相关类
- CFRunloopRef: RunLoop本身
- CFRunLoopModeRef: runloop运行模式
- 【kCFRunLoopDefaultMode(NSDefaultRunLoopMode)】:App的默认Mode,通常主线程是在这个Mode下运行。
- UITrackingRunLoopMode】: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
- 【UIInitializationRunLoopMode】: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
- 【GSEventReceiveRunLoopMode】: 接受系统事件的内部 Mode,通常用不到。
- kCFRunLoopCommonModes (NSRunLoopCommonModes)】: 这个并不是某种具体的 Mode, 可以说是一个占位用的Mode(一种模式组合)。
- CFRunloopSourceRef: runloop要处理的事件源
- Source0:非基于端口Port的事件;(用于用户主动触发的事件,如:点击按钮 或点击屏幕)
- Source1:基于端口Port的事件;(通过内核和其他线程相互发送消息,与内核相关)。
- CFRunloopTimerRed: Timer事件(基于时间的触发器)
- NSTimer会受到Runloop的mode影响
- 与NSTimer相比, GCD定时器不会受到Runloop影响
- CFRunLoopObserverRef: runloop观察者
相当于消息循环的一个监听器, 随时通知外部当前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 //所有状态改变
};
Runloop应用/场景
- NSTimer
- ImageView显示: 控制方法在特定模式下可用
- performSelector
- 常驻线程: 在子线程中开启一个runloop
- AutoreleasePool自动释放池
- UI更新
runloop面试题
Q:runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?
A:runloop: 从字面意思看:运行循环、跑圈,其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)事件。runloop和线程的关系:一个线程对应一个RunLoop,主线程的RunLoop默认创建并启动,子线程的RunLoop需手动创建且手动启动(调用run方法)。RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop。
Q:runloop的mode是用来做什么的?有几种mode?
A:model:是runloop里面的运行模式,不同的模式下的runloop处理的事件和消息有一定的差别。系统默认注册了5个Mode:(1)kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。(2)UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。(3)UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。(4)GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。(5)kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。注意iOS 对以上5中model进行了封装 NSDefaultRunLoopMode、NSRunLoopCommonModes
Q:为什么把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环以后,滑动scrollview的时候NSTimer却不动了?
A nstime对象是在 NSDefaultRunLoopMode下面调用消息的,但是当我们滑动scrollview的时候,NSDefaultRunLoopMode模式就自动切换到UITrackingRunLoopMode模式下面,却不可以继续响应nstime发送的消息。所以如果想在滑动scrollview的情况下面还调用nstime的消息,我们可以把nsrunloop的模式更改为NSRunLoopCommonModes.
Q:苹果是如何实现Autorelease Pool的?
A: Autorelease Pool作用:缓存池,可以避免我们经常写relase的一种方式。其实就是延迟release,将创建的对象,添加到最近的autoreleasePool中,等到autoreleasePool作用域结束的时候,会将里面所有的对象的引用计数器 - autorelease.