isKindOfClass 面试题分析
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; //
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; //
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; //
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; //
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
本代码是在objc源码环境下编译运行的。通过运行可知结果
re1 :1
re2 :0
re3 :0
re4 :0
re5 :1
re6 :1
re7 :1
re8 :1
分析源码可知
类方法isMemberOfClass,实际上是用元类和传入的参数cls做对比
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
实例方法isMemberOfClass,实际上是用类和传入的参数cls做对比
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
类方法isKindOfClass,实际上是一个循环查找元类的父类,然后和参数cls 做对比的过程
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
实例方法isKindOfClass,实际上是一个循环查找类的父类,然后和cls 做对比的过程
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
注意
在最新版mac系统中,在源码isKindOfClass 方法里面打断点调试发现,断点并进不来。使用查看汇编代码的方式可以发现OBJC_EXPORT BOOL
objc_opt_isKindOfClass(id _Nullable obj, Class _Nullable cls)
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
这里发现,objc_opt_isKindOfClass发放支持Mac 10.15之后版本,和iOS 13.0之后的版本。
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
// 当obj是对象的时候,obj->getIsa()实际上等同于 [obj class]。 当obj是类对象的时候,obj->getIsa() 也就是获取类的元类。
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
// 把cls赋值给tcls,并且判断tcls是否存在,如果存在则进行下面的比较。如果if 条件不成立则 tcls 等于 tcls 的父类。
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
通过此方法的注释可知,obj调用isKindOfClass方法的时候会执行当前的方法。