OC消息机制(二)lookUpImp慢速查找
2023-09-12 21:13:08
前言
在之前的文章《OC消息机制(一)objc_msgSend_uncached分析》中,我们深入探讨了消息发送的底层实现,了解到objc_msgSend_uncached函数在消息接收者不是已知类时,会执行慢速查找。在本文中,我们将继续深入挖掘慢速查找的奥秘,分析lookUpImp函数的工作原理,看看它是如何为未知类的消息接收者找到正确的实现方法的。
lookUpImp函数剖析
lookUpImp函数是慢速查找的核心,它负责查找消息接收者类的实现方法。其函数原型如下:
IMP lookUpImp(Class cls, SEL sel)
其中,cls是消息接收者的类,sel是消息选择器。
lookUpImp函数的执行流程主要分为以下几步:
-
查找父类实现方法 :首先,lookUpImp函数会查找cls类的父类实现方法。如果找到,直接返回该实现方法。
-
遍历协议列表 :如果在父类中没有找到实现方法,lookUpImp函数会遍历cls类所遵循的协议列表,依次查找实现方法。如果找到,直接返回该实现方法。
-
调用objc_msgForward :如果在父类和协议中都未找到实现方法,lookUpImp函数会调用objc_msgForward函数,将消息转发给接收者的superclass。
IMP forward_imp = cls->isa->forward_imp;
if (forward_imp)
return forward_imp(cls, sel);
- 使用dispatch_once :如果在superclass中也未找到实现方法,lookUpImp函数会使用dispatch_once函数创建一个单例对象,该对象包含一个方法映射表,其中记录了类名、选择器和实现方法之间的对应关系。
dispatch_once(&objc_lookUpImpCacheOnce, ^{
cache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&objc_lookUpImpCacheValueCallBacks);
});
-
查找缓存 :在方法映射表中查找cls类和sel选择器对应的实现方法。如果找到,直接返回该实现方法。
-
查找Category实现方法 :如果在方法映射表中未找到实现方法,lookUpImp函数会遍历cls类所关联的Category列表,依次查找实现方法。如果找到,直接返回该实现方法。
-
返回nil :如果在所有可能的位置都未找到实现方法,lookUpImp函数会返回nil。
实例分析
让我们以一个具体的例子来分析lookUpImp函数的执行过程。假设我们有一个名为Person
的类,该类有一个名为setName:
的方法。现在我们创建一个Student
类,它继承自Person
类,并覆盖了setName:
方法。
@interface Person : NSObject
- (void)setName:(NSString *)name;
@end
@interface Student : Person
- (void)setName:(NSString *)name;
@end
现在,我们发送一条setName:
消息给一个Student
对象。消息发送流程如下:
-
objc_msgSend 函数调用objc_msgSend_uncached 函数,因为
Student
类不是已知类。 -
objc_msgSend_uncached 函数发现
Student
类不是已知类,于是进入慢速查找。 -
lookUpImp 函数首先查找
Student
类的父类Person
的实现方法,发现有setName:
方法,于是直接返回该实现方法。 -
消息最终调用了
Person
类的setName:
方法。
性能优化
慢速查找是一个相对耗时的操作,尤其是当需要遍历大量的协议和Category时。为了优化性能,我们可以采取以下措施:
- 使用已知类 :尽量使用已知类,避免触发慢速查找。
- 减少协议和Category的使用 :过多的协议和Category会增加慢速查找的开销。
- 使用消息转发 :通过实现
- (id)forwardingTargetForSelector:(SEL)aSelector
方法,可以将消息转发给其他对象,避免慢速查找。
总结
慢速查找是OC消息机制中的一种重要机制,它确保了消息可以发送给未知类的消息接收者。lookUpImp函数是慢速查找的核心,它通过遍历父类、协议和Category等位置来查找实现方法。了解慢速查找的原理有助于我们优化OC程序的性能。
关键词: