iOS RunLoop 源码分析
1.前言
作为一名iOS开发者,写代码的时候RunLoop我们会很少接触到,那么RunLoop到底是个什么玩意呢?具体有什么作用呢?下面我们来分析下RunLoop的本质。
RunLoop源码:https://opensource.apple.com/tarballs/CF/ 找到最新版下载即可。
RunLoop是一个相对抽象的概念,在程序运行过程中循环做一些事情,主要应用于:定时器(Timer)、PerformSelector、GCD Async To Main Queue、事件详情、手势识别、界面刷新、网络请求、AutoreleasePool
我们想象一个场景:为什么App程序启动之后能够持续运行在前台呢?
int main(int argc, char * argv[]){
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([WLBAppDelegate class])); } }
从main函数中可以看出来 是因为UIApplicationMain方法的执行,那么UIApplicationMain内部做了些什么事能保持程序不退出呢?这就是RunLoop的功劳了。
UIApplicationMain的大致实现原理就是:(伪代码)
int retVal = 0;
do {
int message = sleep_and_wait(); //睡眠中等待消息(比如详情点击各种事件)
retVal = proess_message(message);//处理消息,更改返回值,如果为0,代表程序退出,不为0,程序持续运行。
}while(retVal == 0);
RunLoop底层在执行一个while循环,来维持程序的不退出。
2.RunLoop跟线程的关系
RunLoop在OC中有两种获取方式:[NSRunLoop currentRunLoop] 或者C的:CFRunLoopRef
RunLoop个线程是息息相关的,RunLoop是为了线程而生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分, Cocoa 和 CoreFundation 都提供了 run loop 对象方便配置和管理线程的 run loop 。每个线程,包括程序的主线程( main thread )都有与之相应的 run loop 对象,主线程的runloo会在程序开启的时候运行,其他线程需要手动打开才能正常工作
每一个线程都有唯一的一个与之对应的RunLoop对象。
RunLoop保存现在一个全局的Dictionary里面,线程作为key,RunLoop作为value
线程创建并没有RunLoop对象,会在第一次获取的时候,系统创建。
线程结束时销毁RunLoop
3.跟RunLoop相关的类
1.CFRunLoopRef 是C语言的的结构体,是RunLoop的实例对象底层是实现。从源码可以看出
typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
//还有其他的参数,此处我们只研究这个,其他的省略了,完整的可以看源码
};
可以看出,CFRunLoopRef结构体内部保存这一个set集合包含着modes,跟当前model,并且_modes内部应该是一个CFRunLoopModeRef类型的集合
2.CFRunLoopModeRef分析
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
//还有其他的参数,此处我们只研究这个,其他的省略了,完整的可以看源码
};
从源码可以看出,CFRunLoopModeRef内部包含这几种_sources0、_sources1、_observers、_timers集合,源码分析这些集合内部放着CFRunLoopSourceRef、CFRunLoopObserverRef、CFRunLoopTimerRef这些类型,总结出来大致就是这样子的。那么这些都有什么用呢?
RunLoop内部函数作用:
Souces0: 处理一些触摸事件和 perform Selectors方法等。
Sources1:基于Port的线程间的通信。
times: 处理定时器任务
observers: 监听器,监听RunLoop的一些状态的。
内部结构图4.CFRunLoopModeRef种类
1.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode 影响
3.UIInitializationRunLoopMode:在刚启动 App 时进入的第一个 Mode,启动完成后就不再使用
4.GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到
5.kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode
5.RunLoop的执行流程
此分析均是从源码分析得出。
定时器的栈帧分析从栈帧我们可以看出,RunLoop运行的执行了CFRunLoopRunSpecific、 __CFRunLoopRun、__CFRunLoopDoTimers等函数,通过源码CFRunLoop.c文件我们可以看出,他是先是执行了CFRunLoopRun,然后CFRunLoopRunSpecific,再__CFRunLoopRun、等等函数。
RunLoop执行流程根据源码我大致总结了下流程。其实你只要就住一点,RunLoop的运行机制就是运用一个do while循环,根据返回值来保持是否持续来进行运行处理某些事件。还有当无任务时,会进入休眠来,防止CPU的空转,等待消息的唤醒。
希望能帮到一些小伙伴们。