Links

01.RunLoop

App启动

01_动态链接 dyld

  • 将程序依赖的动态链接库递归加载进内存

02_ImageLoader

  • image 表示一个二进制文件(可执行文件或 so 文件),里面是被编译过的符号、代码等
  • ImageLoader 作用是将这些文件加载进内存,且每一个文件对应一个ImageLoader实例来负责加载
  • 在程序运行时它先将动态链接的 image 加载
  • 再从可执行文件 image 递归加载所有符号

03_runtime

  • dyld 开始将程序二进制文件初始化
  • 交由 ImageLoader 读取 image,其中包含了我们的类、方法等各种符号
  • 由于 runtimedyld 绑定了回调,当 image 加载到内存后,dyld 会通知 runtime 进行处理
  • runtime 接手后调用 map_images 做解析和处理,接下来 load_images 中调用 call_load_methods 方法,遍历所有加载进来的 Class,按继承层级依次调用 Class+load 方法和其 Category+load 方法

RunLoop

01_什么是RunLoop

  • 使程序一直运行并接受用户输入
  • 决定程序在何时应该处理那些事件
  • 消息队列
  • 节省CPU时间

02_RunLoop 与 线程

  • 线程RunLoop 之间是一一对应的(可嵌套),其关系是保存在一个全局的 Dictionary
  • 线程结束时销毁, 或手动退出

03_结构

struct __CFRunLoop {
CFMutableSetRef _commonModes; // Set
CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
CFRunLoopModeRef _currentMode; // Current Runloop Mode
CFMutableSetRef _modes; // Set
...
};

Source

RunLoop 的数据源抽象类
RunLoop运行必须要在加入NSTimer或Source0、Sourc1、Observer输入后运行否则会直接退出
Source0: 处理App内部事件, App自行管理
  • 包含了一个函数指针,它并不能主动触发事件
  • 调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理
  • 调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件
Source1: 由RunLoop内核管理, MachPort驱动(进程间通讯)
  • 包含了一个 mach_port 和一个 函数指针
  • 被用于通过内核和其他线程相互发送消息
  • 如解锁/摇晃等

Timer

  • 包含了时长 和 函数指针

Observer

向外部报告 RunLoop 状态的变化
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Loop
};
Observer 与 AutoreleasePool(UIKit)
ObserverRunLoop两次SleepPop旧的并释放对象 Push新的

Mode

struct __CFRunLoopMode {
CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
CFMutableSetRef _sources0; // Set
CFMutableSetRef _sources1; // Set
CFMutableArrayRef _observers; // Array
CFMutableArrayRef _timers; // Array
...
};
  • 在同一时间必须且只能在一个Mode下运行
  • 切换Mode必须退出RunLoop,重启
  • 保证页面滑动流畅的关键
NSDefaultRunLoopMode // 默认状态 空闲状态
UITrackingRunLoopMode // 滑动
//私有Mode
当创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView 时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。这两个 Mode 都已经被标记为Common
特殊Mode:
NSRunLoopCommonModes
有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoopcommonModeItems 中。commonModeItemsRunLoop 自动更新到所有具有 CommonMode 里去。