Objective-C Runtime

5 阅读4分钟

Runtime 是 Objective-C 的灵魂,它是一套用 C + 汇编 实现的运行时库,把 OC 大量语法特性从编译期推迟到运行期处理,让 OC 成为真正的动态语言。

简单说:OC 不关心 “调用哪个函数”,只关心 “发送什么消息”,真正的方法查找、执行、转发全部在运行时决定

例如:[obj doSomething]编译期不会生成直接函数调用,而是转为:objc_msgSend(obj, @selector(doSomething))真正的实现查找与执行,完全在运行时完成。


一、类与对象的底层本质(Runtime 世界观)

Runtime 中一切皆为结构体,对象、类、元类没有神秘之处。

1. 对象(instance / 实例对象)

  • 本质:typedef struct objc_object *id
  • 核心:包含一个 isa 指针,指向自己的类对象(Class)
  • 作用:存储成员变量值

2. 类对象(Class)

  • 本质:typedef struct objc_class *Class

  • 存储:实例方法、属性、成员变量、协议、父类指针

  • 成员:

    • super_class:父类
    • name:类名
    • ivars:成员变量
    • methodLists:实例方法(- 方法)
    • cache:方法缓存(加速调用)
    • protocols:协议

3. 元类(Meta Class)

  • 元类 = 类对象的类
  • 存储:类方法(+ 方法)
  • 继承链:元类也遵循继承
  • 闭环:根元类的 isa 指向自己,super_class 指向根类(NSObject)

一句话总结:

  • 实例对象 isa → 类对象
  • 类对象 isa → 元类对象
  • 元类对象 isa → 根元类
  • 根元类 isa → 自己

二、消息机制:OC 方法调用的真相

OC 没有 “函数调用”,只有消息发送(Message Passing)

消息发送完整流程(objc_msgSend)

  1. 查缓存 cache_t从 isa 找到类 → 优先查方法缓存缓存使用哈希表,命中极快,是 Runtime 高性能核心。
  2. 查当前类方法列表找到 → 执行 IMP → 加入缓存未找到 → 继续
  3. 沿继承链向上查找(父类 → 根类)重复:缓存 → 方法列表仍未找到 → 进入消息转发流程

三、消息转发(三次补救机会)

方法找不到时,Runtime 不会立刻崩溃,而是给 3 次补救机会。

第一道:动态方法决议

+resolveInstanceMethod: / +resolveClassMethod:

  • 机会:动态添加方法实现
  • 用途:懒加载方法、动态生成实现

第二道:快速转发

-forwardingTargetForSelector:

  • 机会:返回一个备用接收者
  • 用途:轻量级多重继承、组件解耦
  • 优点:速度快、无开销

第三道:完整消息转发

  1. -methodSignatureForSelector: 返回方法签名
  2. -forwardInvocation: 处理 NSInvocation
  • 可修改:目标、selector、参数、返回值
  • 可转发给多个对象
  • 最灵活,性能最低

最终崩溃

doesNotRecognizeSelector:未处理 → 抛出 unrecognized selector 异常


四、Method Swizzling(方法调配)

Runtime 最强大、最常用的动态技术,是 AOP 面向切面编程的基石。

核心原理

  • SEL:方法编号(名字)
  • IMP:函数指针(实现)
  • Runtime 本质是:SEL 与 IMP 的映射表

交换两个方法的 IMP,即可实现 “调用 A 方法,执行 B 逻辑”。

核心 API

method_exchangeImplementations(Method1, Method2)

最佳实践

  • +load 中执行
  • 必须用 dispatch_once 保证只交换一次
  • 不要直接替换系统方法,应使用 “自定义方法包裹原方法”
  • 避免滥用、避免冲突

典型用途

  • 全局无痕埋点
  • 页面统计 / 点击统计
  • 系统控件功能增强
  • 防崩溃(数组、字典保护)
  • KVO、weak 底层基于类似原理

五、Runtime 四大动态能力(面试高频)

1. 动态创建类

objc_allocateClassPair``class_addIvar / class_addMethod``objc_registerClassPair可在运行时凭空生成类,常用于:

  • ORM 映射
  • 动态代理
  • KVO 底层(KVO 会动态生成子类)

2. 动态遍历成员变量 & 属性

class_copyIvarList``class_copyPropertyList

用途:

  • JSON -> Model 自动转换(MJExtension、YYModel)
  • 字典转模型
  • 自动化归档解档

3. 关联对象(Category 添加属性)

分类不能直接添加属性,因为不生成 ivar。解决方案:objc_setAssociatedObject``objc_getAssociatedObject

作用:动态绑定对象到某个实例,实现分类 “伪属性”。

4. 动态方法调用与消息转发

完全在运行时决定方法调用,支持热修复、动态路由、组件化通信。


六、Runtime 的真正意义(总结)

Runtime 让 OC 拥有:

  1. 动态类型(运行时才确定对象类型)
  2. 动态方法(运行时才查找实现)
  3. 动态转发(找不到可补救)
  4. 动态修改(交换方法、增删属性、创建类)

它是以下技术的底层基石:

  • KVO / KVC
  • weak 自动置空
  • Category
  • 消息转发
  • AOP
  • 热修复
  • JSON 转模型
  • 组件化 & 路由

理解 Runtime,你就真正理解了 iOS 的底层运行逻辑


总结(极简记忆版)

  1. Runtime = OC 的灵魂,用 C + 汇编实现
  2. 方法调用 = 消息发送,不是函数调用
  3. 对象 → 类 → 元类 → 根元类(闭环)
  4. 查找流程:缓存 → 本类 → 父类 → 转发 → 崩溃
  5. Method Swizzling = 交换 SEL ↔ IMP 映射
  6. 动态能力:创建类、遍历属性、关联对象、消息转发