libobjc.A.dylib 不仅仅是一个动态库,它是 Objective-C 语言的“心脏”。如果没有它,App 里的所有类、方法、内存管理(ARC)以及 Runtime 特性都无法运行。
在 App 的启动生命周期(即 Pre-main 阶段)中,它扮演了“规则制定者”和“舞台搭建者”的角色。我们可以将其工作分为以下三个关键阶段:
1. 加载与映射阶段(Loading & Mapping)
当 dyld(动态链接器)开始加载 App 可执行文件时,它会发现 App 依赖于 libobjc.A.dylib。
-
库的加载:
dyld将libobjc映射到进程的地址空间。 -
注册回调:
libobjc会通过_dyld_objc_notify_register向dyld注册一系列回调函数。- 核心逻辑:每当
dyld加载一个新的镜像(Image,如你的 App 二进制或自定义 Framework)时,它会立刻通知libobjc去处理这个镜像里的 OC 符号。
- 核心逻辑:每当
2. 类与方法的“工业化”处理(Realization)
这是 libobjc 最忙碌的时刻。它需要把磁盘里的静态二进制数据转换成运行时可以直接调用的对象。
-
读取类信息(_read_images) :
- 它会扫描二进制文件中的
__objc_classlist段,找到所有的类声明。 - 方法名唯一化(Sel Refixing) :确保所有
@selector(name)指向唯一的内存地址。
- 它会扫描二进制文件中的
-
类的修复(Realizing Classes) :
- 磁盘里的类数据(
class_ro_t)是只读且静态的。 libobjc会在堆上为每个类分配class_rw_t空间。这个过程会处理 Category(分类) 的合并,将分类中的方法、属性挂载到主类上。
- 磁盘里的类数据(
-
协议与属性注册:将所有的
Protocol和Property注册到运行时的全局哈希表中。
3. 初始化阶段(Initialization)
在 main 函数执行前的临门一脚,libobjc 负责触发代码逻辑。
-
调用
+load方法:libobjc会扫描所有类和分类中的+load方法。- 它会严格遵循继承链:父类优先于子类,主类优先于分类。
- 注意:此时环境是极其敏感的,因为 main 还没跑,很多系统环境可能还没完全就绪。
-
建立 SideTables:初始化我们在前文讨论过的
SideTables结构,准备好接手内存管理。
4. 运行时的持续支持(Runtime Support)
当 App 跨过 main 函数进入运行状态后,libobjc 从“搭建舞台”转为“后台维护”:
- 消息转发(objc_msgSend) :它是这个函数的守护者,负责快速查找方法缓存(Cache)以及找不到方法时的动态转发逻辑。
- ARC 管理:我们讨论过的
objc_retain、objc_release、objc_storeStrong等所有内存操作函数都实现在这个库里。 - 弱引用表维护:处理
__weak的置空和SideTable的同步。
总结:libobjc 的角色定位
| 阶段 | libobjc 的主要任务 | 结果 |
|---|---|---|
| 加载期 | 与 dyld 握手,监听镜像加载 | 建立 Runtime 与系统的联系 |
| 解析期 | 修复类结构、合并分类、重定向 Sel | 将静态代码变为动态对象 |
| 执行期 | 触发 +load,准备内存管理表 | App 逻辑正式准备就绪 |
| 稳定期 | 负责消息分发、ARC 与弱引用 | 保证语言特性正常运行 |
💡 为什么它叫 libobjc.A.dylib?
中间的 .A 是版本号。苹果几十年来一直保持着对这个核心库的高度向下兼容,因此版本号几乎没有变过。所有的 Swift 对象底层也是通过 libobjc 的一些机制与 OC 互通的。