返回

揭秘OC底层奥秘:从cache到objc_msgSend的深层探索

IOS

探索 Objective-C 方法缓存的奥秘

Objective-C (ObjC) 是一种动态语言,它的运行时机制赋予了它强大的灵活性和可扩展性。方法缓存是 ObjC 运行时中一个至关重要的组件,它在方法查找和调用中发挥着关键作用。了解方法缓存的工作原理对于深入理解 ObjC 底层机制至关重要。

方法缓存的本质

方法缓存是与类关联的数据结构,它存储了该类中的所有方法信息。当一个对象发送一条消息时,ObjC 运行时会搜索方法缓存以找到与该消息选择器相对应的实现。如果在方法缓存中找到了方法,则直接执行该实现;否则,运行时会向上搜索父类的方法缓存,直到找到该方法或达到根类 NSObject

方法插入过程

方法在类加载时被插入到缓存中。当编译器编译一个类时,它会生成一个包含该类所有方法信息的符号表。然后,当类被加载到内存中时,ObjC 运行时会遍历符号表,并为每个方法创建一个方法符。这个方法符包含指向方法实现的指针,以及有关方法类型、参数和返回值的信息。然后,方法描述符被插入到该类的缓存中。

方法调用的奥秘

objc_msgSend 是 ObjC 中用于发送消息的关键函数。当一个对象收到一条消息时,objc_msgSend 会被调用来执行该消息。以下是 objc_msgSend 的工作原理:

  1. 查找接收者的类。
  2. 在接收者类的缓存中搜索消息选择器。
  3. 如果在缓存中找到了消息选择器,则调用相应的函数指针。
  4. 如果在缓存中未找到消息选择器,则搜索父类的方法缓存,直到找到消息选择器或达到根类 NSObject

如果在任何类中都找不到消息选择器,objc_msgSend 将返回 nil,并引发“未识别的选择器”异常。

代码示例

以下代码示例演示了方法缓存和 objc_msgSend 如何协同工作:

#import <objc/runtime.h>

@interface MyClass : NSObject
- (void)myMethod;
@end

@implementation MyClass
- (void)myMethod {
    NSLog(@"Hello from myMethod!");
}
@end

int main() {
    // 创建一个 MyClass 对象
    MyClass *myObject = [[MyClass alloc] init];
    
    // 使用 objc_msgSend 调用 myMethod
    objc_msgSend(myObject, @selector(myMethod));
    
    return 0;
}

当我们运行这段代码时,objc_msgSend 会搜索 MyClass 的缓存来查找 myMethod 消息选择器。由于 myMethod 已在类定义中实现,因此它将被插入缓存中。

objc_msgSend 在缓存中找到 myMethod 时,它将调用与 myMethod 关联的函数指针。这将导致 NSLog 语句被打印到控制台。

结论

理解 ObjC 方法缓存及其与 objc_msgSend 的交互对于深入了解 ObjC 的运行时机制至关重要。通过掌握这些概念,我们可以编写出高效且可维护的 ObjC 代码。

常见问题解答

  1. 方法缓存的存储位置是什么?
    方法缓存存储在与类关联的 cache_t 结构中。

  2. 方法描述符包含哪些信息?
    方法描述符包含指向方法实现的指针,以及有关方法类型、参数和返回值的信息。

  3. 如果在方法缓存中找不到消息选择器会发生什么?
    如果在方法缓存中找不到消息选择器,objc_msgSend 将搜索父类的方法缓存,直到找到消息选择器或达到根类 NSObject

  4. 在什么情况下会引发“未识别的选择器”异常?
    如果在任何类中都找不到消息选择器,则会引发“未识别的选择器”异常。

  5. 理解方法缓存的优势是什么?
    理解方法缓存的优势在于它可以帮助我们编写出高效且可维护的 ObjC 代码,并深入了解 ObjC 的运行时机制。