前言
为了增强自己对于RunLoop的理解,故写下该文章来加深自己的印象和理解,以下内容中如果有错误或不准确的地方,欢迎指正。唯有相互学习,才能成长。
什么是RunLoop
RunLoop通常翻译为运行循环,所以顾名思义是指程序要运行过程中循环的去做一些事情。其实在iOS开发中我们很多地方都有用到RunLoop,其中包括:GCD,手势事件处理,Timer定时器,界面刷新等等都和RunLoop息息相关。
RunLoop的底层实现的话相当于do-while循环,当我们创建iOS程序时其实在main.m中就有了这样一种结构,所以这样iOS程序才不会立马就退出,而是一直保持运行状态。
RunLoop在程序中的作用:1、保持程序的持续运行;2、处理事件(定时器,手势等);3、节省CPU资源,在没有事情的时候就休眠等等
RunLoop与线程的关系
RunLoop与线程的关系是一一对应的,即每条线程对应一个RunLoop对象,线程刚刚创建时没有RunLoop对象,RunLoop会在第一次获取时创建;主线程的RunLoop自动创建,子线程没有开启RunLoop;RunLoop会在线程生命周期结束时销毁;RunLoop在内存中存储在以线程作为key,RunLoop为value的全局hash表中。
RunLoop的底层结构
针对于RunLoop相关的操作,iOS提供了两套API,Foundation中的NSRunLoop及CoreFoundation中的CFRunLoopRef,NSRunLoop和CFRunLoopRef都代表着RunLoop对象,前者是基于后者的封装,而后者是开源的,感兴趣可见Core Foundation开源
从底层来看RunLoop中存储这对应的线程_pthread以及mode相关集合。而且CFRunLoopModeRef结构则如下图所示:
所以可以知道RunLoop对象中会存在多个Mode而每个Mide都会包含多个source0,source1,observers及timers。Mode指的是RunLoop运行模式,
如果切换Mode则只能退出当前Loop再重新选择Mode进入。当Mode中没有任何的source0,source1,observers及timers时,RunLoop会立马退出。
日常开发中常用的两种Mode:
NSDefaultRunLoopMode:App默认Mode,通常主线程运行在该Mode下;
UITrackingRunLoopMode:界面跟踪Mode,用于UIScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响。
Source0:触摸事件处理,performSelector:onThread:
Source1:用于Port线程间通信,系统时间的捕捉
Times:NSTimer,performSelector:withObject:afterDelay:
Observers:监听RunLoop状态,UI刷新,自动释放池
RunLoop运行逻辑
- 通知Observers:进入Loop
- 通知Observers:即将处理Timers
- 通知Observers:即将处理Sources
- 处理Blocks
- 处理Source0(可能会再次处理Block)
- 如果存在Source1,跳转至第8步
- 通知Observers:开始休眠(等待消息唤醒)
- 通知Observers:结束休眠(被某个消息唤醒)
1>处理Timer
2>处理GCD,Async To Main Queue
3>处理Source1 - 处理Blocks
- 根据前面执行结果决定如何操作
1>回到2步
2>退出Loop - 通知Observers:退出Loop
项目中使用
RunLoop其实在项目中常见的使用方式:线程的保活,可以采取RunLoop方式实现,比如AF中常驻线程就是采用该方式实现的;Timers滑动ScrollView失效问题,是根据RunLoop运行模式来解决。