前言
OC中的自省函数有两个 isKindOfClass & isMemberOfClass,开发中应该经常会用到.
函数作用是用来判断对象是否是某个类或者其子类对象,平时使用时多为实例对象判断是否为类对象的实例,今天在准备面试题目时,发现对这两个函数理解的还不是很深入,所以查询源码后分享一下实现原理,面试题也会放在测试环节说明.
相关知识
如果想理解自省函数,需要对oc对象的底层逻辑有所了解,主要是对象、类对象、元类对象之间的关系以及superClass指针和isa指针,这里不做扩展,自行了解
相关函数object_getClass() & class()
- object_getClass() : 函数的作用是获取入参对象的isa指针指向的对象,例如如果入参为实例对象,它的isa指针指向类对象,如果入参是类对象,则isa指向mate Class元类对象.如果入参为元类对象,则isa指向root mate Class
Class object_getClass(id obj) {
if(obj) return obj->getIsa();
else return Nil;
}
- class() : 函数用来获取类对象,分为实例方法和类方法.实例函数返回他的类对象,类函数返回类对象本身
- Class class() {
return object_getClass(self);
}
+ Class class() {
return self;
}
isKindOfClass
函数作用: 判断对象是否为入参类型的对象,实现源码如下:
+ (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;
}
函数分为类函数和实例函数,二者的区别在于循环条件初始值的不同,
对于类函数来说,Class tcls = object_getCLass(self) 初始值为类对象self的isa指针指向的对象(即为mateClass元类对象),简单说就是是从元类对象开始对比,然后沿着superClass指针查找,直接指向nil或者找到,结束遍历
对于实例函数来说,Class tcls = [self class] 是从实例对象的类对象开始比较的,而上面也提到,[self class] 其实等同于调用object_getClass(self) 返回类对象
总结两个方法,初始值都是从调用对象的isa指针指向的对象开始判断,
isMemberOfClass
函数作用类似于isKindOfClass,区别在于,只会判断入参和调用对象的isa指针是否相同,而不会遍历查找,代码如下:
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
根据class()的实现,两个函数实现是一样的,都是return object_getClass(self) == cls;
相关面试题
了解了两个函数的实现之后,再来看面试题就会轻松很多了,可以先自己判断返回值,然后查看打印结果对比验证
// CFPerson : NSObject
// NSObject.class()调用 + class()返回类对象本身,
// isKindOfClass 是从对象isa指向的对象开始遍历,即mateClass,二者不同,掉用superClass然后继续对比,mateClass的superClass指向root mateClass时,再调用superClass则指向了root Class(OC中为NSObject);NSObject == NSObject,所以返回1
NSLog(@"%hhd", [[NSObject class] isKindOfClass:[NSObject class]]);
// 实例对象的class指向类对象,还是NSObject类对象 和NSObject.class等价
NSLog(@"%hhd", [[obj class] isKindOfClass:[NSObject class]]);
// 调用实例方法kindofclass, obj.class->NSObject 对比NSObject,返回1
NSLog(@"%hhd", [obj isKindOfClass:[NSObject class]]);
// CFPerson对比元类对象,返回false,通过superclass继续查找知道NSObject->nil, 最终返回0
NSLog(@"%hhd", [[CFPerson class] isKindOfClass:[CFPerson class]]);
//类方法, CFPerson.mateclass 和CFPerson类对象比较,返回0, CFPerson.mateClass.superclass->NSObject->nil 最终返回0
NSLog(@"%hhd", [[student class] isKindOfClass:[CFPerson class]]);
// 实例函数, CFPerson = cls tls = CFPerson
NSLog(@"%hhd", [student isKindOfClass:[CFPerson class]]);
// 元类不等于类对象,所以返回0
NSLog(@"%hhd", [[NSObject class] isMemberOfClass:[NSObject class]]);
// 同上,元类不等于类对象,返回0
NSLog(@"%hhd", [[obj class] isMemberOfClass:[NSObject class]]);
// 类对象等于,返回1
NSLog(@"%hhd", [obj isMemberOfClass:[NSObject class]]);
// 元类对象不等于类对象,返回0
NSLog(@"%hhd", [[CFPerson class] isMemberOfClass:[CFPerson class]]);
// 同上返回0
NSLog(@"%hhd", [[student class] isMemberOfClass:[CFPerson class]]);
// 类对象等于,返回1
NSLog(@"%hhd", [student isMemberOfClass:[CFPerson class]]);
最后
由于刚开始学习写博客,文笔水平有限,如果你有不同的理解或想法,欢迎交流