OC Runtime 详解
OC 消息机制
Objective-C 语言 中,对象方法调用都是类似 [receiver selector]; 的形式,其本质就是让对象在运行时发送消息的过程。
1 | Receiver *receiver = [[Receiver alloc] init]; |
- 编译阶段:[receiver selector]; 方法被编译器转换为: objc_msgSend(recevier,selector,org1,org2,…)
- 运行时阶段:消息接受者 recever 寻找对应的 selector。
- 通过 recevier 的 isa 指针 找到 recevier 的 Class(类);
- 在 Class(类) 的 cache(方法缓存) 的散列表中寻找对应的 IMP(方法实现);
- 如果在 cache(方法缓存) 中没有找到对应的 IMP(方法实现) 的话,就继续在 Class(类) 的 method list(方法列表) 中找对应的 selector,如果找到,填充到 cache(方法缓存) 中,并返回 selector;
- 如果在 Class(类) 中没有找到这个 selector,就继续在它的 superClass(父类)中寻找;一旦找到对应的 selector,直接执行 recever 对应 selector 方法实现的 IMP(方法实现)。
- 若找不到对应的 selector,消息被转发或者临时向 recever 添加这个 selector 对应的实现方法,否则就会发生崩溃。
OC 类与对象
OC Class
1 |
|
- isa:是一个不能为空(null)的Class对象,它指向自己的创建者(类或元类)。
- super_class:是一个可以为空的Class对象,它指向当前类的父类。
- ivars:是实例变量列表,保存着该类的所有实例变量。
- methodLists:是方法列表,保存着实例方法和类方法。
- protocols:是协议列表,保存该类的协议方法。
- cache:是一个缓存,用于缓存最近使用过的方法。
OC Object
1 | /// Represents an instance of a class. |
一个 Object(对象)唯一保存的就是它所属 Class(类) 的地址。
OC 实例对象、类、元类关系
- instance 实例对象是由类创建出来的对象,根据实际情况,内存中存在多份。实例对象通过isa指针指向类(对象)。
- class 类是实例对象的描述,保存了实例对象的定义。同时,类也是元类的对象(类对象),内存中只有一份。类对象通过isa指针指向元类(对象)。
- metaclass 元类是类对象的描述。元类对象的isa指针指向根元类,即由根元类创建的元类。根元类的isa指针指向自己,形成一个封闭的内循环。

实线的箭头(→ superclass)代表父类指针,虚线的箭头(→isa)代表isa指针,从上至下的深色方框依次代表:根类-> 父类-> 子类,从上至下每层的含义:
- 第一层:
- isa指针: 根类的实例对象(instance Of Root class)的isa指向根类(Root class),根类的isa指针指向根元类(Root meta class),根元类的isa指向它本身。
- superclass指针:根类(Root class)的superclass指向nil,根元类的父类指向根类。
- 第二层:
- isa指针:父类的实例对象(instance of superclass)的isa指向父类(superclass),父类的isa指向父元类,父元类的isa指向根元类。
- superclass指针:父类的superclass指向根类,父元类的superclass指向根元类。
- 第三层:
- isa指针:子类实例对象(instance of subclass)的isa指向子类,子类的isa指向子元类,子元类的isa指向根元类。
- superclass指针:子类的superclass指向父类,子元类的superclass指向父元类。
1 | // 继承关系: SubClass -> SuperClass -> NSObject |

- 当发送消息给实例对象时,runtime函数会在此实例对象所属类的实例方法列表中查找方法实现(IMP)并调用,此为实例方法调用。
- 当发送消息给类对象时,runtime函数会在此实例对象所属类的元类的方法列表中查找方法实例(IMP)并调用,此为类方法调用。
NSObject
NSObject 是大部分Objective-C类继承体系的根类。
OC runtime
Runtime 中为我们提供了一系列 API 来获取 Class (类)的 成员变量( Ivar )、属性( Property )、方法( Method )、协议( Protocol ) 等。我们可以通过这些方法来遍历一个类中的成员变量列表、属性列表、方法列表、协议列表。
Runtime 的 API 都定义在 <objc/runtime.h> 中,函数的命名规则如下:
- 对对象进行操作的方法一般以object_开头
- 对类进行操作的方法一般以class_开头
- 对类或对象的方法进行操作的方法一般以method_开头
- 对成员变量进行操作的方法一般以ivar_开头
- 对属性进行操作的方法一般以property_开头开头
- 对协议进行操作的方法一般以protocol_开头
1 | /* |
获取 Class
1 | // 通过实例对象获取 |
创建对象
1 | // 通过 class 创建对象 |
方法调用
1 | SEL selector = NSSelectorFromString(@"FuncName"); |
获取类的成员变量
成员变量定义如下:
1 | typedef struct objc_ivar *Ivar |
成员变量有如下相关函数:
1 | // 获取所有成员变量 |
获取类的属性
属性的定义:
1 | typedef struct objc_property *objc_property_t; |
属性有如下相关函数:
1 | // 获取所有属性 |
获取类的方法
方法的定义:
1 | /// An opaque type that represents a method in a class definition. |
- SEL 是一个指向 objc_selector 结构体的指针,其实是一个保存方法名的字符串。
- IMP 的实质是一个函数指针,所指向的就是方法的实现。IMP用来找到函数地址,然后执行函数。
方法有如下相关函数:
1 | // 获取所有方法 |
参考文档
https://juejin.cn/post/6912007846125273101
https://halfrost.com/objc_runtime_isa_class/
https://juejin.cn/post/6844903474908364808
https://www.jianshu.com/p/1fa5cf16c6db
https://wjerry.com/2018/05/%E5%AF%B9%E8%B1%A1-%E7%B1%BB-%E5%85%83%E7%B1%BB-isa%E6%8C%87%E9%92%88%E4%B9%8B%E9%97%B4%E7%9A%84%E7%88%B1%E6%81%A8%E6%83%85%E4%BB%87/
https://zhangxiaom.github.io/2018/06/26/isa%E6%8C%87%E9%92%88%E4%B8%AD%E9%9A%90%E8%97%8F%E7%9A%84%E9%BB%91%E9%AD%94%E6%B3%95/