isKindOfClass 和 isMemberOfClass
日常开发中,经常使用-isKindOfClass
来判断对象是否是某个类或其父类(继承链上的类),但很少使用-isMemberOfClass
,它们之间到底有什么不同,本篇文章就从objc源码
来剖析它们的区别。
一、案例
在objc源码
(请参考这篇文章)中新建Person
类,继承自NSObjec
,空实现,然后main
函数修改如下:
// main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class objCls = [NSObject class];
BOOL ret1 = [(id)objCls isKindOfClass:[NSObject class]];
BOOL ret2 = [(id)objCls isMemberOfClass:[NSObject class]];
NSLog(@"\nret1 = %d\nret2 = %d", ret1, ret2);
Class pCls = [Person class];
BOOL ret3 = [(id)pCls isKindOfClass:[Person class]];
BOOL ret4 = [(id)pCls isMemberOfClass:[Person class]];
NSLog(@"\nret3 = %d\nret4 = %d", ret3, ret4);
NSObject *obj = [[NSObject alloc] init];
BOOL ret5 = [obj isKindOfClass:[NSObject class]];
BOOL ret6 = [obj isMemberOfClass:[NSObject class]];
NSLog(@"\nret5 = %d\nret6 = %d", ret5, ret6);
Person *person = [[Person alloc] init];
BOOL ret7 = [person isKindOfClass:[Person class]];
BOOL ret8 = [person isMemberOfClass:[Person class]];
NSLog(@"\nret7 = %d\nret8 = %d", ret7, ret8);
}
return 0;
}
运行程序后,控制台输出如下:
2020-02-08 20:27:45.681409+0800 objc-debug[1378:33202]
ret1 = 1
ret2 = 0
2020-02-08 20:27:45.682017+0800 objc-debug[1378:33202]
ret3 = 0
ret4 = 0
2020-02-08 20:27:45.682176+0800 objc-debug[1378:33202]
ret5 = 1
ret6 = 1
2020-02-08 20:27:45.682305+0800 objc-debug[1378:33202]
ret7 = 1
ret8 = 1
Program ended with exit code: 0
这个结果似乎有点奇怪,请先细细品味一番...
二、源码解读
上面的代码,实际调用了四个方法,类方法:
+isKindOfClass:
-
+isMemberOfClass:
和实例方法: -
-isKindOfClass:
-isMemberOfClass:
1 +isKindOfClass:
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
object_getClass(self)
即self->isa
的指向,从源码可以看出,[NSObject class]
就是NSObject
类本身,其isa
指向是NSObject元类
,然后沿着NSObject元类
的继承链向上逐级判断,NSObject元类
是继承自NSObject
的。因此,
BOOL ret1 = [(id)objCls isKindOfClass:[NSObject class]]
这行代码的判断路径是NSObject元类
->NSObject
,自己和自己相等,所以ret1 = YES
。
Person->isa
是Person元类
,Person元类
继承自NSObject元类
,NSObject元类
继承自NSObject
,那么
BOOL ret3 = [(id)pCls isKindOfClass:[Person class]];
这行代码的判断路径是Person元类
->NSObject元类
->NSObject
,即Person != NSObject
,ret3 = NO
。
2 +isMemberOfClass:
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
直接判断isa
的指向是否和入参相等,NSObject元类
自然不等于NSObject
,所以ret2 = NO
。Person元类
也不等于Person
,ret4 = NO
。
3 -isKindOfClass:
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
这和类方法唯一的区别就是循环条件的初始值是tcls = [self class]
,而不是tcls = object_getClass(self)
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
class
方法也分类方法和实例方法,类方法直接返回self
,实例方法返回的是self->isa
的指向。此处我们是用实例对象调用的,所以会执行实例方法-class
,这样以来,其实和类方法+isKindOfClass:
的实现时相同的。
BOOL ret5 = [obj isKindOfClass:[NSObject class]];
obj->isa
就是NSObject
,这行代码的判断路径是NSObject
->nil
,第一步就命中,ret5 = YES
。
BOOL ret7 = [person isKindOfClass:[Person class]];
person->isa
就是Person
,这行代码的判断路径是Person
->NSObject
->nil
。ret7 = YES
4 -isMemberOfClass:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
即判断self->isa
是否等于入参。
BOOL ret6 = [obj isMemberOfClass:[NSObject class]];
这行代码翻译过来其实是obj->isa
== NSObject
,ret6 = YES
。
BOOL ret8 = [person isMemberOfClass:[Person class]];
同理,person->isa
== Person
,ret8 = YES
。
三、总结
+isKindOfClass:
和-isKindOfClass:
会从self->isa
的指向开始判断,沿着self->isa
的继承链一直到NSObject
,其中任一个和入参相等,就返回YES
。
+isMemberOfClass:
和-isMemberOfClass:
直接判断self->isa
是否和入参相等。
最后放上这张经典的isa
指向和继承关系图:

转载自:https://juejin.cn/post/7084154382903672840