developer:hi,runloop 初次见面,交个朋友吧?
runloop :你好,很高兴认识你!
developer:我先自我介绍一下,我叫iOS Developer,你呢?
runloop :我叫runloop,你的有些同伴叫我跑圈,我觉得叫我循环运行比较合适!
developer:我听我的同伴说你有点不是很好相处啊,是不是真的?
runloop :不会吧,我只是喜欢在幕后工作,并不是很爱抛头露面而已!
developer:那你的主要工作内容都是哪些呢?
runloop :我啊?主要是为了让程序一直处于运行状态,同时在运行的状态下处理一系列的事件,比如触摸事件、定时器事件、selecter事件,在这样的高强度的工作下还得维持良好的状态!
developer:听起来很屌的样子!跟我说说你跟线程的关系吧,听说你两是生死相依的兄弟!
runloop :线程啊,我两谁也离不开谁,他出生了我也就诞生了,当他被销毁的时候我也就扑街了。
developer:那平时我们拜访线程挺容易的,要怎么去拜访你呢?
runloop :有两种方式
1、Foundation框架下的NSRunLoop
2、Core Foundation框架下得CFRunLoopRef
不论NSRunLoop和CFRunLoopRef都代表着我
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象

看到这张图没?
这张图就代表着一个完整的runloop,每一个runloop中都包含以下5个类:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
如果没有这5个类,runloop将会直接退出
developer:能详细的介绍以下他们吗?
runloop :当然。
CFRunLoopModeRef
CFRunLoopModeRef 类并没有对外暴露,只是通过 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 影响
UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
CFRunLoopSourceRef
CFRunLoopSourceRef 是事件产生的地方。Source有两个版本:Source0 和 Source1。
• Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
• Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。
CFRunLoopObserverRef
CFRunLoopObserverRef 是观察者,每个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
};
上面的 Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。
使用
- (void)observer{
// 创建observer CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@"----监听到RunLoop状态发生改变---%zd", activity);
});
// 添加观察者:监听RunLoop的状态
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
// 释放Observer
CFRelease(observer);}
特别注意
/* CF的内存管理(Core Foundation)
1.凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release * 比如CFRunLoopObserverCreate
2.release函数:CFRelease(对象);
*/
CFRunLoopTimerRef
CFRunLoopTimerRef 是基于时间的触发器,它和 NSTimer 是toll-free bridged 的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。
RunLoop 的内部逻辑
根据苹果在文档里的说明,RunLoop 内部的逻辑大致如下:

可以看到,实际上 RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。
developer:那我应该怎么样劳烦你去处理一些问题呢?
runloop :解密-神秘的RunLoop这里有记录怎么去使用runloop
developer:如何才能与你深交呢?
runloop :深入理解RunLoop
网友评论