参考原文:https://blog.ibireme.com/2015/05/18/runloop/
要点如下:
1、事件循环实质就是一个do-while循环,而RunLoop就是实现了do-while循环方法,并管理其需要处理的事件和消息的一个对象
2、RunLoop和线程是一一对应的,实际上就是字典的键值对(线程是key,RunLoop是value),由一个全局字典保存
3、RunLoop是懒加载的,只有第一次调用时才会被创建
4、RunLoop包含多个model,每次调用RunLoop的主函数时只能指定其中一个model;需要切换model时只能先退出当前循环,在重新指定一个model进入
5、每个model中包含若干Source/Timer/Observer:
CFRunLoopTimerRef:包括一个时间长度和一个回调。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调
Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。
6、NSTimer:
RunLoop结构体中有一个_commonModes,每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 “Common” 标记的所有Mode里
这就是NSTimer需要使用CommonModel的原因,因为kCFRunLoopDefaultMode 和 UITrackingRunLoopMode都被标记为”Common”属性,DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态
7、自动释放池:
主线程的RunLoop注册了两个Observer,一个用来监测刚进入Loop(Entry)时创建自动释放池,另一个监测准备进入休眠(BeforeWaiting)和即将退出Loop(Exit) 两个状态,准备进入休眠状态时会释放池中需要自动释放的对象,并创建新池
即自动释放池是在每次事件循环即将进入休眠状态时进行自动释放操作
8、页面刷新:
当一个UIView或CALayer需要更新时,会被标记为待处理并添加到一个全局的容器中,当RunLoop即将进入休眠(BeforeWaiting)或退出(Exit)时,再去刷新所有待处理的视图
即页面刷新是在每次事件循环即将进入休眠时刷新
9、网络请求:
当创建NSURLConnection调用start时,start内会获取currentRunLoop,并在currentRunLoop的defaultModel中添加四个source(需要手动触发),用来负责回调代理方法和处理Cookie;
当开始网络传输时,NSURLConnection会创建两个新线程(com.apple.NSURLConnectionLoader 、com.apple.CFSocket.private),CFSocket用于处理底层socket连接,NSURLConnectionLoader使用RunLoop(scource1)接收socket事件,并通知delegate所在的线程唤醒RunLoop