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]的底层

  1. 消息接收者仍然是子类对象
  2. 从父类开始查找方法的实现

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类型,因此不一样。

这道题不仅考察了isKindOfClassisMemberOfClass
还考察了NSArray类簇的知识点

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值