本文属笔记性质,是对kirito_song和冰凌天两位文章学习和转载
文章具体地址:
小码哥iOS学习笔记第十七天: Runloop基本认识
MJiOS底层笔记--Runloop
什么是RunLoop?
RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象
- 没有消息需要处理时,休眠以避免资源占用:从用户态到内核态切换
- 有消息处理时,立刻被唤醒:从内核态到用户态的切换
应用范畴
- 定时器(Timer)、PerformSelector
- GCD Async Main Queue
- 事件响应、手势识别、界面刷新
- 网络请求
- AutoreleasePool
- 其他
作用
- 保持程序的持续运行
- 处理App中的各种事件(比如触摸事件、定时器事件等)
- 节省CPU资源,提高程序性能:该做事时做事,该休息时休息
- 其他
RunLoop的数据结构
NSRunloop是CFRunloop的封装,提供了面向对象的API
RunLoop相关的类有五个
- CFRunLoopRef
- CFRunLoopRef实际类型是__CFRunLoop

- 主要成员结构有下面几个
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef; struct __CFRunLoop { pthread_t _pthread; //代表线程,表明线程和RunLoop的关系是一一对应关系 CFMutableSetRef _commonModes; //NSMutableSet<NSString *>字符串类型的集合,存储的是字符串,记录所有标记为common的mode CFMutableSetRef _commonModeItems;//存储所有commonMode的item(source、timer、observer) CFRunLoopModeRef _currentMode;//当前运行的mode CFMutableSetRef _modes;//存储的是CFRunLoopModeRef, }; _modes中存放的是CFRunLoopModeRef类型数据, 其中就有_currentMode, 只不过_currentMode是当前使用的mode CommonMode不是实际存在的一种Mode,把一些Mode打上Common标记 是同步Source/Timer/Observer到多个Mode中的一种技术方案 CommonModes:一个 Mode 可以将自己标记为 Common 属性(通过将其 ModeName 添加到 RunLoop 的 commonModes 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems里的 Source/Observer/Timer 同步到具有 Common 标记的所有 Mode 里。 _commonModeItems代表着能在Common模式下工作的单元 - CFRunLoopRef实际类型是__CFRunLoop
- CFRunLoopModeRef(RunLoop运行模式)
- CFRunLoopModeRef的结构如下

- 主要成员结构有下面几个
typedef struct __CFRunLoopMode *CFRunLoopModeRef; struct __CFRunLoopMode { CFStringRef _name; //模式的名称 CFMutableSetRef _sources0; CFMutableSetRef _sources1; CFMutableArrayRef _observers; CFMutableArrayRef _timers; }; 其中的_sources0和_sources1是CFRunLoopSourceRef类型数据 其中的_observers是CFRunLoopObserverRef类型数据 其中的_timers是CFRunLoopTimerRef类型数据- 说明
1.CFRunLoopModeRef代表RunLoop的运行模式 2.一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer 3.RunLoop启动时只能选择其中一个Mode,作为currentMode 4.如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入 不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响 5.如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出- CFRunLoopModeRef有5种运行模式,常见的是前两种
1.kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行 2.UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView追踪触摸滑动,保证界面滑动时不受其他 Mode 影响 3.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode 4.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到 5.kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode - CFRunLoopModeRef的结构如下
- CFRunLoopSourceRef
- source0 需要手动唤醒线程
- source1 具备唤醒线程的能力
- CFRunLoopTimerRef
- CFRunLoopObserverRef
他们之间的关系(一对多模式)

监听RunLoop状态
- 我们可以通过给RunLoop添加Observer的方式监听RunLoop的状态, 使用下面这个函数
/**
给RunLoop添加Observer
@param rl 目标RunLoop
@param observer 需要添加的Observer
@param mode 监听状态
*/
void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFRunLoopMode mode);
- 可以通过下面的函数创建Observer
/**
创建Observer
@param allocator 分配器
@param activities 需要监听的状态
@param repeats 是否重复监听
@param order 顺序
@param callout 回调函数
@param context 附加对象
@return 创建好的Observer
*/
CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context);
- RunLoop的状态有下面几种
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),//即将进入RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), //即将处理Timer
kCFRunLoopBeforeSources = (1UL << 2), //即将处理Source
kCFRunLoopBeforeWaiting = (1UL << 5), //即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), //即将从休眠中唤醒
kCFRunLoopExit = (1UL << 7), //即将进入RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
- 监听的应用
void observeRunLoopActicities(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"kCFRunLoopEntry");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"kCFRunLoopBeforeTimers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"kCFRunLoopBeforeSources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"kCFRunLoopBeforeWaiting");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"kCFRunLoopAfterWaiting");
break;
case kCFRunLoopExit:
NSLog(@"kCFRunLoopExit");
break;
default:
break;
}
}
- (void)viewDidLoad {
// 创建Observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, observeRunLoopActicities, NULL);
// 添加Observer到RunLoop中
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
// 释放
CFRelease(observer);
}
RunLoop运行逻辑(转自:小码哥iOS学习笔记第十七天: Runloop运行逻辑)
- 在RunLoop的CFRunLoopModeRef中, 主要有以下几个成员变量
Source0
触摸事件处理
performSelector:onThread:
Source1
基于Port的线程间通信
系统事件捕捉
Timers
NSTimer
performSelector:withObject:afterDelay:
Observers
用于监听RunLoop的状态
UI刷新(BeforeWaiting)
Autorelease pool(BeforeWaiting)
- RunLoop运行逻辑
01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop
-
图解RunLoop运行逻辑

-
RunLoop的休眠原理

