下面代码的结果?
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];
答案:
除了第一个是YES,其他三个都是NO。
在推测结果之前,首先要明白两个问题。isKindOfClass和isMemberOfClass的区别是什么?
isKindOfClass:class,调用该方法的对象所属的类,继承者链中包含传入的class则返回YES。
isMemberOfClass:class,调用改方法的对象所属的类,必须是传入的class则返回YES。
我们从Runtime源码的角度来分析一下结果。
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
平时开发过程中只会接触到对象方法的isKindOfClass和isMemberOfClass,但是在NSObject类中还隐式的实现了类方法版本。不只这两个方法,其他NSObject中的对象方法,都有其对应的类方法版本。因为在OC中,类和元类也都是对象。这四个调用由于都是类对象发起调用的,所以最终执行的都是类方法版本。
先把Runtime的对象模型拿出来,方便后面的分析。
第一次调用方是NSObject类对象,调用isKindOfClass方法传入的也是类对象。因为调用类的class方法,会把类自身直接返回,所以还是类对象自己。
然后进入到for循环中,会从NSObject的元类开始遍历,所以第一次NSObject meta class != NSObject class,匹配失败。第二次循环将tcls设置为superclass的NSObject class,NSObject class == NSObject class,匹配成功。
NSObject能匹配成功,是因为这个类比较特殊,在第二次获取superclass的时候,NSObject元类的superclass就是NSObject的类对象,所以会匹配成功。而其他三种匹配,则都会失败,各位同学可以去自己分析一下剩下三种。