深入浅出Runloop

书上得来终觉浅、绝知此事要躬行。本文参考来源:http://mrpeak.cn/blog/ios-runloop/

一.Runloop 简介

什么是runloop ,简单来说、就是一个 do while 死循环、每次loop 都会执行下面的事情,当没有任务的时候 就会进入休眠。

{
 performmask()  //执行任务
 callout_to_observer()  //通知外部
 sleep() //休眠
}

Performtask()

每次loop都会都会执行若干个task,在我们runloop执行的任务又有哪些呢,总结下来有下面5种

  • DoBlocks() //CFRunLoopPerformBlock
  • DoSources0() //CFRunLoopSourceContext,CFRunLoopSourceCreate,CFRunLoopAddSource
  • DoSources1() // source1 并不对开发者开放,系统会使用它来执行一些内部任务,比如渲染 UI
  • DoTimers() //CFRunLoopDoTimers
  • DoMainQueue() //_dispatch_main_queue_callback_4CF(msg)这一步执行与runloopModel 没有关系。

callout_to_observer

主要是来通知外部观察者当前runloop执行了那些任务,当前runloop处于那种状态。有如下三种方式 以及相对应调用的函数。

  • DoObserver-Timer
    通过如下函数调用
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
  • DoObserver-Source0
    通过如下函数调用
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

这是上述五种执行任务方式中 ,两种可以注册 observer 的,其他几个都不支持,mainQueue,source1,block 都不行。所以理论上,是没有办法准确测量各个任务执行的时长的。

  • DoObserver-Activity

DoObserver-Activity是runloop 用来通知外部自己当前处于那个状态。根据源码runloop有如下几种状态

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),//开始进入某个Model
    kCFRunLoopBeforeTimers = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2),
    kCFRunLoopBeforeWaiting = (1UL << 5),//开始休眠之前
    kCFRunLoopAfterWaiting = (1UL << 6),//休眠中恢复
    kCFRunLoopExit = (1UL << 7),//退出切换model
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

sleep()

没有任务就休眠,有任务就执行。

Runloop 完整执行流程

根据RunLoop简介 我们已经将RunLoop 细分为三类,但是RunLoop并非是按上述划分顺序执行的,而他们是相互交错运行的。

深入浅出Runloop_第1张图片
完整流程图 .png

根据上面的流程图 呈现了 五种任务执行和六种通知外部函数的执行过程。

RunLoop Model

RunLoopModel 非常重要,核心点。

在一个程序运行中有一个主线程的RunLoop 和很多个子线程的Runloop。主线程的runLoop默认是开启状态,在我们的RunLoop里面又存在多个RunLoopModel,每个Model 都在执行上面描述的过程。在上面的任务和外部通知中大部分函数是和我们Model进行绑定执行的。RunLoop的结构源码如下

struct __CFRunLoopMode {
    ...
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    CFIndex _observerMask;
    ...
};

RunLoopModel 的种类分为common和private两大类共五种。
公共的

  • kCFRunLoopDefaultMode //App的默认Mode,通常主线程是在这个Mode下运行
  • UITrackingRunLoopMode //界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  • kCFRunLoopCommonModes //集合模式 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode

私有的

  • UIInitializationRunLoopMode //在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode
  • GSEventReceiveRunLoopMode //接受系统事件的内部 Mode,通常用不到

我们常用的也就是公共里面的两种 kCFRunLoopDefaultMode 和 UITrackingRunLoopMode

RunLoop的作用

  • 开发者通过RunLoop执行自己添加的任务
  • 分析整个项目主线程的运行情况、检测卡顿等问题。

RunLoop和线程的关系

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
  • 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁

你可能感兴趣的:(深入浅出Runloop)