iOS 底层原理

iOS RunLoop 源码分析

2018-05-07  本文已影响33人  咖啡豆8888

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的空转,等待消息的唤醒。

希望能帮到一些小伙伴们。

上一篇 下一篇

猜你喜欢

热点阅读