取决于这个 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 修复逻辑。