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 里去。

Last updated