对于isKindOfClass & isMemberOfClass的理解,我们可以通过下面的示例来看:
- iskindOfClass & isMemberOfClass 类方法调用
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[WZPerson class] isKindOfClass:[WZPerson class]];
BOOL re4 = [(id)[WZPerson class] isMemberOfClass:[WZPerson class]];
NSLog(@" \n re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
- iskindOfClass & isMemberOfClass 实例方法调用
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[WZPerson alloc] isKindOfClass:[WZPerson class]];
BOOL re8 = [(id)[WZPerson alloc] isMemberOfClass:[WZPerson class]];
NSLog(@" \n re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
最终的打印结果为:
2020-11-30 11:29:01.705430+0800 WZTarget[3249:117241]
re1 :1
re2 :0
re3 :0
re4 :0
2020-11-30 11:29:01.705830+0800 WZTarget[3249:117241]
re5 :1
re6 :1
re7 :1
re8 :1
Program ended with exit code: 0
源码分析
isKindOfClass源码:
//--isKindOfClass---类方法、对象方法
//+ isKindOfClass:第一次比较是 获取类的元类 与 传入类对比,再次之后的对比是获取上次结果的父类 与 传入 类进行对比
+ (BOOL)isKindOfClass:(Class)cls {
// 获取类的元类 vs 传入类
// 根元类 vs 传入类
// 根类 vs 传入类
// 举例:LGPerson vs 元类 (根元类) (NSObject)
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
//- isKindOfClass:第一次是获取对象类 与 传入类对比,如果不相等,后续对比是继续获取上次 类的父类 与传入类进行对比
- (BOOL)isKindOfClass:(Class)cls {
/*
获取对象的类 vs 传入的类
父类 vs 传入的类
根类 vs 传入的类
nil vs 传入的类
*/
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
isMemberOfClass源码:
//-----类方法
//+ isMemberOfClass : 获取类的元类,与 传入类对比
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
//-----实例方法
//- isMemberOfClass : 获取对象的类,与 传入类对比
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
总结:
isKindOfClass:
- 类方法:类的元类(isa)-> 根元类(父类)-> 根类(父类)-> nil(父类) VS 传入类
- 对象方法:对象的类 -> 父类 -> 根类 -> nil VS 传入类
注:然后通过断点调试,isMemberOfClass 的类方法 和 实例方法的流程是正常的,会走到上面分析的源码,而isKindOfClass根本不会走到上面分析的源码中(!!!注意这里,这是一个坑点),而是会走到下面这个源码中,其类方法和实例方法都是走到objc_opt_isKindOfClass方法源码中isMemberOfClass:
- 类方法:类的元类 VS 传入类
- 对象方法:对象的类 VS 传入类

- objc_opt_isKindOfClass方法源码如下
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
//获取isa,
//如果obj 是对象,则isa是类,
//如果obj是类,则isa是元类
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
// 如果obj 是对象,则在类的继承链进行对比,
// 如果obj是类,则在元类的isa中进行对比
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
为什么会这样呢?主要是因为在llvm中编译时对其进行了优化处理
- 如果obj 是对象,则isa是类,则在类的继承链进行对比
- 如果obj是类,则isa是元类,则在元类的isa中进行对比
案例代码执行结果分析
根据源码的分析,来分析代码执行的结果为什么是0或者1
使用类方法结果解析:
- re1 :1 ,是 NSObject 与 NSObject 的对比,使用 +isKindOfClass
NSObject(传入类,即根类) vs NSObject的元类即根元类-- 不相等
NSObject(传入类,即根类) vs 根元类的父类即根类 -- 相等,返回1
- re2 :0 ,是 NSObject 与 NSObject 的对比,使用 +isMemberOfClass
NSObject根类(传入类) vs NSObject的元类即根元类 -- 不相等
- re3 :0 ,是 LGPerson 与 LGPerson 的对比,使用 +isKindOfClass
LGPerson(传入类) vs LGPerson的元类即元类LGPerson -- 不相等
LGPerson(传入类) vs 元类LGPerson的父类即根元类 -- 不相等
LGPerson(传入类) vs 根元类的父类即根类 -- 不相等
LGPerson(传入类) vs 根类的父类即 nil -- 不相等
- re4 :0 ,是 LGPerson 与 LGPerson 的对比,使用 +isMemberOfClass
LGPerson(传入类) vs 元类 -- 不相等
使用实例方法结果解析:
- re5 :1 ,是 NSObject对象 与 NSObject 的对比,使用 -isKindOfClass
NSObject(传入类,即根类) vs 对象的isa即NSObject根类 -- 相等
- re6 :1 ,是 NSObject对象 与 NSObject 的对比,使用 -isMemberOfClass
NSObject(传入类,即根类) vs 对象的类即NSObject根类 -- 相等
- re7 :1 ,是 LGPerson对象 与 LGPerson 的对比,使用 -isKindOfClass
LGPerson(传入类) vs 对象的isa即LGPerson -- 相等
- re8 :1 ,是 LGPerson对象 与 LGPerson 的对比,使用 -isMemberOfClass
LGPerson(传入类) vs 对象的类即LGPerson -- 相等
网友评论