• 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类簇的知识点

  • 相关阅读:
    GD32E507移植FreeRTOS
    FreeRTOS学习笔记——任务基础知识
    FreeRTOS学习笔记——系统配置
    am335x WG209 wifi模块自动配置的脚本
    树莓派学习笔记——Systemd进程启动
    am335x WG209 wifi模块驱动移植
    树莓派学习笔记——搭建Samba服务端
    树莓派学习笔记——内核编译
    一个复制文件的Shell脚本
    Makefile学习笔记——Makefile通配符的使用
  • 原文地址:https://www.cnblogs.com/r360/p/15891490.html
Copyright © 2020-2023  润新知