前言
我们在面试有关分类的有关方法的时候都知道如下的两个知识点:
- 同名的方法,分类会替换主类中的方法
- 如果多个分类有同名的方法,那么这个方法是随机调用的
理论基础:
runtime加载类的时候,会先加载类(先去递归加载其父类),后加载所有的Category.
在Objective-C Automatic Reference Counting (ARC)中也有类似的描述:
Categories allow methods (but not ivars) to be declared post hoc on an arbitrary class; the methods in the category’s
@implementation
will be dynamically added to that class’s method tables which the category is loaded at runtime, replacing those methods in case of a collision.
Category中的方法在冲突的时候会发生替换
验证
输出一个类的方法列表RXRuntimeUtil
中:
+ (void)printMethodListWithClass:(Class)cls
{
NSMutableArray *array = [NSMutableArray new];
[array addObject:@""];
[array addObject:@"method list:"];
[array addObject:@"methodName address"];
unsigned int count;
Method *methodList = class_copyMethodList(cls, &count);
for (unsigned int i = 0; i < count; i++) {
Method method = methodList[i];
NSString *methodName = NSStringFromSelector(method_getName(method));
IMP methodIMP = method_getImplementation(method);
NSString *str = [NSString stringWithFormat:@"%@ %p", methodName, methodIMP];
[array addObject:str];
}
NSLog(@"%@", [array componentsJoinedByString:@"\n"]);
}
定义一个父类:RXARCCategoryMethodParentObject
@implementation RXARCCategoryMethodParentObject
- (void)print
{
NSLog(@"RXARCCategoryMethodParentObject");
}
@end
定义一个子类RXARCCategoryMethodSubObject
继承于RXARCCategoryMethodParentObject
@implementation RXARCCategoryMethodSubObject
- (void)print
{
NSLog(@"RXARCCategoryMethodSubObject");
}
@end
给刚刚那个子类定义两个Category
@implementation RXARCCategoryMethodSubObject (C1)
- (void)print
{
NSLog(@"C1");
}
@end
和
@implementation RXARCCategoryMethodSubObject (C2)
- (void)print
{
NSLog(@"C2");
}
@end
测试代码:
- (void)_test_category_method
{
[RXRuntimeUtil printMethodListWithClass:[RXARCCategoryMethodParentObject class]];
[RXRuntimeUtil printMethodListWithClass:[RXARCCategoryMethodSubObject class]];
}
测试结果:
method list:
methodName address
print 0x10d04b8c0
method list:
methodName address
print 0x10d020a00
print 0x10d01e960
print 0x10cfe96d0
结论
在测试结果中发现,居然出现了3个print函数,这3个分别是:
子类的,C1分类的,C2分类的,跟父类的那个没有关系,父类的是在Parent的方法表中。
猜测是:也许以前的版本是替换,现在的版本是追加了,根据消息发送机制,只不过会优先搜索到Category中的方法而已,实际上的主类中那个方法依然存在。