重新认识 RunLoop

Tags
iOS
Date
Mar 21, 2021
 

我对 RunLoop 的理解

notion image
我的理解,仔细分析这个图,RunLoop 的设计主要是为了异步处理事件。外部的事件触发只是唤醒 RunLoop,并非立即同步执行处理。唤醒后进入循环才逐一处理事件。
 
RunLoop 有两种类型的 Source:
  1. Input Sources,传递异步事件。
    1. Port-Based Sources (Source1),能够主动唤醒 RunLoop。
    2. Custom Input Sources (Source0),需要手动唤醒 RunLoop。
  1. Timer Sources,传递同步事件。
 
Observer 的状态流转:
所有状态:
kCFRunLoopEntry → kCFRunLoopBeforeTimers → kCFRunLoopBeforeSources → kCFRunLoopBeforeWaiting → kCFRunLoopAfterWaiting → kCFRunLoopExit
休眠之前循环处理事件:
kCFRunLoopBeforeTimers → kCFRunLoopBeforeSources → kCFRunLoopBeforeTimers
唤醒之后开始处理事件:
kCFRunLoopAfterWaiting → kCFRunLoopBeforeTimers
 
大部分的UI事件回调都在 kCFRunLoopBeforeSources 之后,kCFRunLoopBeforeWaiting 之前,堆栈如图
 
notion image
 
但,- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath,这个方法执行在 kCFRunLoopBeforeWaiting 的 observer 里,堆栈如图
notion image
 
一个 RunLoop 同一时刻只能处于一个 mode 中。common mode 只是一种标记,表示 common mode 中的 modes 共享 sources、timers、observers。
 
iOS 的 modes:
NSDefaultRunLoopMode
UITrackingRunLoopMode
NSRunLoopCommonModes(默认包括NSDefaultRunLoopMode和UITrackingRunLoopMode)
UIInitializationRunLoopMode
GSEventReceiveRunLoopMode
 
RunLoop的休眠可以看成是用户状态到内核状态的切换,而唤醒RunLoop就是内核状态到用户状态的切换。
 

RunLoop的实际应用

系统应用:
  • AutoreleasePool,创建了两个 observer ,在监听里创建和释放自动释放池。
  • 硬件事件响应,从 __CFRunLoopDoSource1 传到 __CFRunLoopDoSource0
  • 手势识别
  • 界面更新
  • CFRunLoopPerformBlock,performSelecter
  • NSTimer = CFRunLoopTimerRef
  • dispatch_async(dispatch_get_main_queue(), block)
 
其他应用:
  • NSURLConnection(线程保活)
  • AsyncDisplayKit(界面刷新)
  • 卡顿监控
  • 通过密集执行代码改为一次RunLoop执行一次,可避免卡顿(如tableview图片加载)
 

RunLoop 的官方文档

 

RunLoop 的参考文章

深入理解RunLoop
RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理。之后会介绍一下在 iOS 中,苹果是如何利用 RunLoop 实现自动释放池、延迟回调、触摸事件、屏幕刷新等功能的。 一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑是这样的: 这种模型通常被称作 Event Loop 。 Event Loop 在很多系统和框架里都有实现,比如 Node.js 的事件处理,比如 Windows 程序的消息循环,再比如 OSX/iOS 里的 RunLoop。实现这种模型的关键点在于:如何管理事件/消息,如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。 所以,RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后,就会一直处于这个函数内部 "接受消息->等待->处理" 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。 OSX/iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。 CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API
深入理解RunLoop

RunLoop 的相关源码

 

Loading Comments...