3-1.【OC】【Runtime】libobjc.A.dylib 在整个 App 启动生命周期中扮演了什么角色?

0 阅读3分钟

libobjc.A.dylib 不仅仅是一个动态库,它是 Objective-C 语言的“心脏”。如果没有它,App 里的所有类、方法、内存管理(ARC)以及 Runtime 特性都无法运行。

在 App 的启动生命周期(即 Pre-main 阶段)中,它扮演了“规则制定者”和“舞台搭建者”的角色。我们可以将其工作分为以下三个关键阶段:


1. 加载与映射阶段(Loading & Mapping)

dyld(动态链接器)开始加载 App 可执行文件时,它会发现 App 依赖于 libobjc.A.dylib

  • 库的加载dyldlibobjc 映射到进程的地址空间。

  • 注册回调libobjc 会通过 _dyld_objc_notify_registerdyld 注册一系列回调函数。

    • 核心逻辑:每当 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(分类) 的合并,将分类中的方法、属性挂载到主类上。
  • 协议与属性注册:将所有的 ProtocolProperty 注册到运行时的全局哈希表中。


3. 初始化阶段(Initialization)

main 函数执行前的临门一脚,libobjc 负责触发代码逻辑。

  • 调用 +load 方法

    • libobjc 会扫描所有类和分类中的 +load 方法。
    • 它会严格遵循继承链:父类优先于子类,主类优先于分类
    • 注意:此时环境是极其敏感的,因为 main 还没跑,很多系统环境可能还没完全就绪。
  • 建立 SideTables:初始化我们在前文讨论过的 SideTables 结构,准备好接手内存管理。


4. 运行时的持续支持(Runtime Support)

当 App 跨过 main 函数进入运行状态后,libobjc 从“搭建舞台”转为“后台维护”:

  • 消息转发(objc_msgSend) :它是这个函数的守护者,负责快速查找方法缓存(Cache)以及找不到方法时的动态转发逻辑。
  • ARC 管理:我们讨论过的 objc_retainobjc_releaseobjc_storeStrong 等所有内存操作函数都实现在这个库里。
  • 弱引用表维护:处理 __weak 的置空和 SideTable 的同步。

总结:libobjc 的角色定位

阶段libobjc 的主要任务结果
加载期dyld 握手,监听镜像加载建立 Runtime 与系统的联系
解析期修复类结构、合并分类、重定向 Sel将静态代码变为动态对象
执行期触发 +load,准备内存管理表App 逻辑正式准备就绪
稳定期负责消息分发、ARC 与弱引用保证语言特性正常运行

💡 为什么它叫 libobjc.A.dylib

中间的 .A 是版本号。苹果几十年来一直保持着对这个核心库的高度向下兼容,因此版本号几乎没有变过。所有的 Swift 对象底层也是通过 libobjc 的一些机制与 OC 互通的。