今天从源码来彻底了解这俩方法的区别。
相信很多人应该都遇到过类似的面试题,日常开发中也用这俩方法做过不少判断,比如后台返回的数据是不是数组,是不是null,是不是字典,某个实例是不是指定的控制器等等。
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[Person class] isKindOfClass:[Person class]];
BOOL res4 = [[Person class] isMemberOfClass:[Person class]];
NSLog(@"%d %d %d %d", res1, res2, res3, res4);
BOOL res5 = [[[NSObject new]class] isKindOfClass:[NSObject class]];
BOOL res6 = [[[NSObject new]class] isMemberOfClass:[NSObject class]];
BOOL res7 = [[[Person new]class] isKindOfClass:[Person class]];
BOOL res8 = [[[Person new]class] isMemberOfClass:[Person class]];
NSLog(@"%d %d %d %d", res5, res6, res7, res8);
BOOL res9 = [[NSObject new] isKindOfClass:[NSObject class]];
BOOL res10 = [[NSObject new] isMemberOfClass:[NSObject class]];
BOOL res11 = [[Person new] isKindOfClass:[Person class]];
BOOL res12 = [[Person new] isMemberOfClass:[Person class]];
NSLog(@"%d %d %d %d", res9, res10, res11, res12);
这里面牵扯到的源码如下:
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); 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;
}
先捋一下这几个方法
- 类方法
+ (Class)class
返回的是他自身,即类对象。 - 实例方法
- (Class)class
返回的object_getClass(self)
,即实例对象的isa
,对这部分有点了解的应该知道,实例对象的isa
指向的就是类对象。 - 类方法
+ (BOOL)isMemberOfClass:(Class)cls
,判断的是类对象的isa
(即元类)是否等于cls
。 - 实例方法
- (BOOL)isMemberOfClass:(Class)cls
,判断的是实例对象调用class方法的返回值(即如上2,也就是类对象
)是否与cls
相等。 - 类方法
+ (BOOL)isKindOfClass:(Class)cls
,这里会先取类对象的isa (self->ISA())
,类对象的isa
指向的是元类,判断元类是否与cls相等,而且这里是一个for循环,不相等的话会往元类的父类(tcls = tcls->superclass)
循环去查找对比。 - 实例方法
- (BOOL)isKindOfClass:(Class)cls
,这里是先取实例对象的isa ([self class]),也就是类对象
,判断类对象与cls是否相等,不相等的话会往类对象的父类(tcls = tcls->superclass)
循环去查找对比。
对实例对象,类对象,元类不了解的可以看我前面的文章
捋清楚了上面几个方法之后,再来看看开头的面试题,是不是豁然开朗了。
下面我们一个个来康康。
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
这里可以相当于[NSObject类对象 isKindOfClass NSObject类对象]
,即上述第5点,判断的是NSObject的元类
是否等于类对象
,很明显,肯定不等于,这是2个东西,接着去和元类的父类对比,那么按道理也是不相等的,但是如果对关系图了解透彻的同学应该知道,根元类的父类就是根类对象,那里这里会不太一样,即NSObject的元类
的父类等于NSObject类对象
,那么最终会变成比较NSObject类对象是否等于NSObject类对象
,所以这里 res1的结果为 1.BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
这里可以相当于[NSObject类对象 isMemberOfClass NSObject类对象]
,即上述第3点。判断的是NSObject的元类是否等于NSObject类对象
,那必然不等于,所以这里 res2的结果为 0.BOOL res3 = [[Person class] isKindOfClass:[Person class]];
这里可以相当于[Person类对象 isKindOfClass Person类对象]
,即上述第3点。判断的是Person的元类
是否等于类对象
,必然不等于,接着去和元类的父类对比,也一样是不等于的。这里不会向res1一样,最终也只是判断NSObject类对象是否等于Person类对象
,所以res3 结果为 0。BOOL res4 = [[Person class] isMemberOfClass:[Person class]];
这里可以相当于[Person类对象 isMemberOfClass Person类对象]
,即上述第3点。判断的是Person的元类是否等于Person类对象
,那必然不等于,所以这里 res2的结果为 0.BOOL res5 = [[[NSObject new]class] isKindOfClass:[NSObject class]];
这里首先调用实例对象的class
方法(即上述第2点),那么就相当于[NSObject类对象 isKindOfClass NSObject类对象]
,那就是res1
的情况了,所以结果同res1
。BOOL res6 = [[[NSObject new]class] isMemberOfClass:[NSObject class]];
那么这里一样结果同res2
。BOOL res7 = [[[Person new]class] isKindOfClass:[Person class]];
结果同res3
。BOOL res8 = [[[Person new]class] isMemberOfClass:[Person class]];
结果同res4
。BOOL res9 = [[NSObject new] isKindOfClass:[NSObject class]];
这里相当于[NSObject实例对象 isKindOfClass NSObject类对象]
,即如上述第6点,判断的是NSObject类对象与NSObject类对象是否相等
,那必然是相等的。所以res9的结果为 1。BOOL res10 = [[NSObject new] isMemberOfClass:[NSObject class]];
这里相当于[NSObject实例对象 isMemberOfClass NSObject类对象]
,即如上述第4点,判断的也是NSObject类对象与NSObject类对象是否相等
,那必然是相等的,所以res10的结果为 1。BOOL res11 = [[Person new] isKindOfClass:[Person class]];
结果同 res9。BOOL res12 = [[Person new] isMemberOfClass:[Person class]];
结果同 res10。
最终打印结果与理论是相符的。