3-8.【OC】【Runtime】Swift 类是否也经过 Objective-C Runtime 的类加载流程?

4 阅读2分钟

取决于这个 Swift 类是否继承自 NSObject,以及它如何被调用。

Swift 的类加载机制实际上是一套“双轨制”。为了兼容强大的 Cocoa 生态,它既保留了与 Objective-C Runtime 互通的能力,又拥有一套更高效的纯 Swift 元数据(Metadata)系统。


1. 继承自 NSObject 的 Swift 类

如果你定义一个类如 class MyViewController: UIViewController {},它本质上是一个 Objective-C 类

  • 加载流程:它会完全经历前面提到的 _read_images、类修复、分类合并等流程。
  • 注册libobjc 会在 Mach-O 的 __objc_classlist 中发现它,并将其注册到 Runtime 的类表中。
  • 特性支持:它可以正常触发 +load(虽然 Swift 不推荐使用)和 +initialize

2. 纯 Swift 类(Root Class)

如果你定义一个不继承自任何基类的类 class MyPureSwiftClass {},情况就不同了。

  • 加载流程:它不会在启动时通过 libobjc 进行复杂的类修复(Realization)。
  • 元数据(Metadata) :Swift 编译器会为它生成一份 V-Table(虚函数表)。当程序需要用到这个类时,Swift 运行时会直接通过偏移量查找元数据,而不是通过 OC 的字符串哈希查找。
  • Runtime 注册:为了反射(Mirror)等特性的需要,纯 Swift 类依然会被加载,但它的结构(TargetClassMetadata)比 OC 的 objc_class 更轻量。

3. 关键差异:静态 vs 动态

特性Swift 纯类 (Pure Swift)继承自 NSObject 的类
方法分发静态分发 / V-Table (快)消息转发 objc_msgSend (慢但灵活)
类加载开销极低(按需加载元数据)较高(需经历 Pre-main 修复流程)
Runtime 介入仅在需要反射或桥接时全程介入
内存布局紧凑的 Metadata 结构标准的 objc_class 结构

4. 为什么要了解这个?

这对启动优化至关重要:

  • 瘦身建议:如果你的 App 中有大量不再使用 OC 特性的类,将其改为纯 Swift 类(断开与 NSObject 的继承),可以减少 libobjc 在 Pre-main 阶段的处理负担,从而加快启动速度
  • 动态性:纯 Swift 类默认不支持 Method Swizzling。如果你需要进行方法交换,必须在方法前加上 dynamic 标识并继承自 NSObject

5. 一个特殊的特例:@objc 修饰符

即便一个类是纯 Swift 类,如果你给它的某个方法加了 @objc,Swift 编译器会为该方法生成一个“跳板函数”(Thunk),让 OC Runtime 能够看到这个方法。但这个类本身的底层结构依然是 Swift 元数据结构,而不是完整的 objc_class


总结

Swift 类并不是“逃离”了 OC Runtime,而是**“按需使用”**。

  • 继承自 NSObject = 完整的 OC 类加载流程。
  • 纯 Swift 类 = 轻量级的 Swift 元数据加载,跳过了昂贵的 Pre-main 修复逻辑。