Category的同名方法覆盖并不是真的其他同名方法就消失了,而是因为系统调用方法的时候根据方法名在method_list中查找方法,找 到第一个名字匹配的方法之后就不继续往下找了。所以每次调用的都是method_list中最前面的同名方法。实际其他同名方法还在 method_list中。
所以我们可以根据selector查找到所有的同名method,然后调用
static inline void __invoke_all_method(id self, SEL selecotr)
{
//1. 根据self,获取class Class class = object_getClass(self);
//2. 获取方法列表 uint count; Method *methodList = class_copyMethodList(class, &count);
//3. 遍历方法列表 for (int i = 0; i < count; i++)
{
Method method = methodList[i];
//4. 根据SEL查找方法
if (!sel_isEqual(selecotr, method_getName(method)))
{ continue; }
//5. 获取方法的实现
IMP implement = method_getImplementation(method);
//6. 直接调用方法的实现
((void(*)(id,SEL))implement)(self, selecotr);
}
}
+ (void)invokeAllClassMethodWithSelector:(SEL)selector
{
__invoke_all_method(self, selector);
}
根据刚刚介绍的原理,我们封装了一个通过selector调用所有同名method的方法。
- 根据self,获取class,如果self是实例方法的self,这里获取的是普通的class,如果self是类方法的self,这里获取的是metaClass。实例方法存放在普通class中,类方法存放在metaClass中。了解更多请看iOS开发RunTime之函数调用
- 通过class_copyMethodList获取class的方法列表。如果class传的是metaClass,获取的是类方法的方法列表,如果class是普通class,获取的是实例方法的方法列表。
- 遍历methodList
- 根据SEL查找method
- 获取IMP
- 直接调用IMP
在系统的+initialize中,我们用invokeAllClassMethodWithSelector调用自定义 的+categoryInitialize。这时候,在category的+categoryInitialize中添加属性,就不怕Category覆盖了。
网友评论