Runtime的本质(四)---[super class]、isKindOfClass、isMemberOfClass
来一道面试题:
@interface YZPerson : NSObject
@end
@interface YZStudent : YZPerson
@end
@implementation YZStudent
- (instancetype)init
{
if (self = [super init]) {
NSLog(@"[self class] = %@", [self class]);
NSLog(@"[self superclass] = %@", [self superclass]);
NSLog(@"[super class] = %@", [super class]);
NSLog(@"[super superclass] = %@", [super superclass]);
}
return self;
}
@end
[self class]=YZStudent,
[self superclass]=YZPerson,
这两个感觉没问题,问题是后两个
按照一般思维
super指的是YZPerson,[super class]= YZPerson,[super superclass]=NSObject。
运行结果是:
[self class] = YZStudent
[self superclass] = YZPerson
[super class] = YZStudent
[super superclass] = YZPerson
为什么呢?
为了更好的分析,我们新加并执行了下面的对象方法
YZStudent.m文件
- (void)run
{
[super run];
NSLog(@"YZStudent-%s", __func__);
}
然后,我们将YZStudent.m文件转换为底层代码
static void _I_YZStudent_run(YZStudent * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("YZStudent"))}, sel_registerName("run"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_91_sht6gqgs1xj8b0_gczwc0ygc0000gn_T_YZStudent_f45099_mi_0, __func__);
}
简化过后:
[super run];
等价于:
objc_msgSendSuper((__rw_objc_super){
self,
class_getSuperclass(objc_getClass("YZStudent"))
}, @selector(run));
而class_getSuperclass的定义是:
Class class_getSuperclass(Class cls)
{
if (!cls) return nil;
return cls->superclass;
}
所以,等价于:
objc_msgSendSuper((__rw_objc_super){
self,
[YZPerson class])
}, @selector(run));
__rw_objc_super可以在源码中查找到定义:
也就是,objc_super有两个参数:
receiver:消息接收者
super_class:消息接收者的父类
objc_msgSendSuper的定义:
objc_msgSendSuper的消息接收者,为__rw_objc_super的第一个元素self。然后从__rw_objc_super第二个元素[YZPerson class]开始寻找方法。
也就是说,[super run];的消息接收者是self,然后从[YZPerson class]开始寻找run方法。由于YZPerson里面有run方法,因此,打印结果为:
YZPerson--[YZPerson run]
YZStudent--[YZStudent run]
看明白了[super run],我们再回到一开始的面试题。
NSLog(@"[super class] = %@", [super class]);
我们要知道,class不在student类对象中,也不在person的类对象中,而是在NSObject中。
而class的方法定义代码为:
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
[super class],消息接收者self(student),首先从super(person)中找class方法,没有。去父类NSObject中找,找到,返回的self是消息接收者student,因此,打印的结果是[super class] = YZStudent。
而superclass的方法定义代码为:
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
[self class] = objc_msgSend(self, @selector(class));
[self superclass] = objc_msgSend(self, @selector(superclass));
[super class] = objc_msgSendSuper({self, [YZPerson class]}, @selector(class));
[super superclass] = objc_msgSendSuper({self, [YZPerson class]}, @selector(superclass));
[super message]的底层
- 消息接收者仍然是子类对象
- 从父类开始查找方法的实现
isKindOfClass与isMemberOfClass
一道面试题:
NSLog(@"%d", [[NSObject class] isKindOfClass:[NSObject class]]);
NSLog(@"%d", [[NSObject class] isMemberOfClass:[NSObject class]]);
NSLog(@"%d", [[NSObject class] isKindOfClass:[YZPerson class]]);
NSLog(@"%d", [[NSObject class] isMemberOfClass:[YZPerson class]]);
NSLog(@"%d", [[YZPerson class] isKindOfClass:[NSObject class]]);
NSLog(@"%d", [[YZPerson class] isMemberOfClass:[NSObject class]]);
NSLog(@"%d", [[YZPerson class] isKindOfClass:[YZPerson class]]);
NSLog(@"%d", [[YZPerson class] isMemberOfClass:[YZPerson class]]);
首先,在源文件中,搜索文件NSObject开头的文件,可以找到NSObject.mm文件,里面有isKindOfClass和isMemberOfClass的具体实现:
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (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;
}
可以看出,
isMemberOfClass的作用是看左右两个类型是否相等。
isKindOfClass的作用是如果左边<=右边返回1,即左边与右边相等或者左边是右边的子类返回1。
如此说来,我们可以解决对象方法的两种类型的面试题:
id person = [[YZPerson alloc] init];
NSLog(@"%d", [person isKindOfClass:[NSObject class]]);
NSLog(@"%d", [person isMemberOfClass:[NSObject class]]);
NSLog(@"%d", [person isKindOfClass:[YZPerson class]]);
NSLog(@"%d", [person isMemberOfClass:[YZPerson class]]);
运行结果:
1//YZPerson是NSObject子类,返回1
0//YZPerson != NSObject,返回0
1//YZPerson = YZPerson,返回1
1//YZPerson = YZPerson,返回1
我们需明白,前面的person,无论通过-isKindOfClass还是-isMemberOfClass,传进去的都是[self class]。即person传进去,等号左边是[person class] ,也就是YZPerson。
然后,我们看一下调用两个函数的类方法:
NSLog(@"%d", [YZPerson isKindOfClass:[NSObject class]]);
NSLog(@"%d", [YZPerson isMemberOfClass:[NSObject class]]);
NSLog(@"%d", [YZPerson isKindOfClass:[YZPerson class]]);
NSLog(@"%d", [YZPerson isMemberOfClass:[YZPerson class]]);
运行结果:
1//object_getClass(YZPerson) NSObject,解释在下面
0//object_getClass(YZPerson) != NSObject,返回0
0//object_getClass(YZPerson) > YZPerson,返回0
0//object_getClass(YZPerson) != YZPerson,返回0
解释:
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
前面的YZPerson,无论通过+isKindOfClass还是+isMemberOfClass,传进去的都是object_getClass((id)self)。即YZPerson传进去,等号左边是object_getClass(YZPerson),类对象的isa指针指向meta class(元类)
这四个结果,下面三个容易解释,而第一个为啥返回的是1?看这张图就明白了:
object_getClass(YZPerson)是元类,跟NSObject不相等,但是object_getClass(YZPerson)的superclass的superclass是NSObject,俩左右相等,因此为1。
回到一开始的面试题:
NSLog(@"%d", [[NSObject class] isKindOfClass:[NSObject class]]);
NSLog(@"%d", [[NSObject class] isMemberOfClass:[NSObject class]]);
NSLog(@"%d", [[NSObject class] isKindOfClass:[YZPerson class]]);
NSLog(@"%d", [[NSObject class] isMemberOfClass:[YZPerson class]]);
NSLog(@"%d", [[YZPerson class] isKindOfClass:[NSObject class]]);
NSLog(@"%d", [[YZPerson class] isMemberOfClass:[NSObject class]]);
NSLog(@"%d", [[YZPerson class] isKindOfClass:[YZPerson class]]);
NSLog(@"%d", [[YZPerson class] isMemberOfClass:[YZPerson class]]);
结果:
1//object_getClass(NSObject) < NSObject,返回1
0//object_getClass(NSObject) != NSObject,返回0
0//object_getClass(NSObject) > YZPerson,返回0
0//object_getClass(NSObject) != YZPerson,返回0
1//object_getClass(YZPerson) < NSObject,返回1
0//object_getClass(YZPerson) != NSObject,返回0
0//object_getClass(YZPerson)是元类 > YZPerson是类对象,返回0
0//object_getClass(YZPerson) != YZPerson,返回0
有没有发现,isKindOfClass右边是NSObject,必然是1。
在使用的时候,需要注意:
左边是实例对象,右边是类对象
左边是类对象,右边是元类对象
JD面试题
问下面两个值分别是多少?
BOOL isEqual = [[NSArray array] isKindOfClass:[NSObject class]];
BOOL isEqual1 = [[NSArray array] isMemberOfClass:[NSArray class]];
首先,根据前面所学,第一个为YES,没有问题
第二个,[NSArray array] 就是一个对象,取对象的class,那么就是NSArray
右边,[NSArray class],就是NSArray,那么,也是YES。
但是,打印结果却是:
YES
NO
第二个为何是NO?
我们尝试打印一下
会发现,left是__NSArray0类型,right是NSArray类型,因此不一样。
这道题不仅考察了isKindOfClass
和isMemberOfClass
还考察了NSArray类簇的知识点