11-2.【OC】【protocol】Protocol 在 runtime 中是如何表示的?

0 阅读3分钟

在 Objective-C Runtime 中,Protocol(协议)被视为一种特殊的对象。它不像 Class 那样拥有自己的实例变量空间,而是一套关于方法的“蓝图”或“描述符”

在底层,协议由结构体 protocol_t 表示。


1. 核心数据结构:protocol_t

每个协议在编译后都会生成一个 protocol_t 结构体。它的设计目标是完整描述协议的继承关系以及它所包含的四类方法。

C++

struct protocol_t : objc_object {
    const char *mangledName;             // 协议名称
    struct protocol_list_t *protocols;    // 继承的其他协议列表
    method_list_t *instanceMethods;      // @required 实例方法
    method_list_t *classMethods;         // @required 类方法
    method_list_t *optionalInstanceMethods; // @optional 实例方法
    method_list_t *optionalClassMethods;   // @optional 类方法
    struct property_list_t *instanceProperties; // 协议定义的属性
    // ... 还有 size 和一些元数据
};

关键点:

  • 四种列表分立:Runtime 将 requiredoptional 方法完全分开存储。这也是为什么 respondsToSelector: 在运行时可以根据方法存在的列表来推断协议实现情况。
  • 协议继承protocols 指针允许协议像类一样形成继承链(例如 UITableViewDelegate 继承自 UIScrollViewDelegate)。

2. 协议的注册与存储

协议在 Runtime 中是单例存在的。

  1. 全局 Hash 表:所有的协议在加载镜像(Images)时,都会被注册到一个全局的哈希表 protocols 中。
  2. 获取方式:当你使用 @protocol(MyProtocol) 时,编译器会将其转化为对全局表中该协议对象的引用。如果该协议尚未加载,Runtime 会负责查找并返回。

3. 类与协议的关联

当一个类(Class)声明遵循某个协议时,这个信息会被记录在类的 class_readonly_t(只读数据)中。

  • protocol_list_t:类的结构体中有一个指向协议列表的指针。
  • 校验逻辑:当你调用 conformsToProtocol: 时,Runtime 会递归遍历该类及其父类的协议列表,以及这些协议继承的子协议。

4. 运行时动态创建协议

由于协议在底层是结构体,Runtime 允许我们在程序运行期间动态创建一个协议:

  1. objc_allocateProtocol:分配内存并命名。
  2. protocol_addMethodDescription:向 requiredoptional 列表中添加方法描述。
  3. objc_registerProtocol:一旦注册,协议就变为只读,可以被类引用。

5. 总结:Protocol 的本质

维度Protocol 在 Runtime 中的表现
本质描述方法的结构体(蓝图),不承载具体实现。
存储存在于全局哈希表中,通过名称唯一标识。
方法查找objc_msgSend 不通过协议查找方法,而是直接找类的方法列表。协议仅用于类型检查和文档约束。
与类的关系类持有指向协议对象的指针列表。

💡 深度启发

协议本身不存储 IMP(函数指针) 。它只存储方法名(SEL)和类型编码(Type Encoding)。这就是为什么协议可以定义规范,但具体的执行逻辑必须由遵循该协议的类通过其 method_list 来提供。