美文网首页iOS题
runtime面试题分析

runtime面试题分析

作者: 卖馍工程师 | 来源:发表于2020-09-15 18:06 被阅读0次

    前言

    此篇博客需要用到 isasuperclass 的指向流程分析,如果你对此还不是很清晰,建议你先通过这篇博客 看透 isa 了解一下 ;如果你已有所掌握,在这里,我们先做一个简短的回顾。

    isa与superclass流程图

    isa 的指向:对象的 isa 指向 ; 类的 isa 指向 元类;元类的 isa 指向 根元类;根元类的 isa 指向 自己

    类的superclass 的指向:类的 superclass 指向 父类, 父类的 superclass 指向 根类 ,根类的superclass 指向 nil

    元类的superclass 的指向:元类的 superclass 指向 父类的元类,父元类的 superclass 指向 根类的元类 根元类的 superclass 指向 根类 根类的 superclass 指向 nil

    示例分析

    1. 下面的代码输出什么内容?
      // Person 继承 NSObject
      @implementation Person : NSObject
    
      BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       
      BOOL re2 = [(id)[Person class]   isKindOfClass:[Person class]]; 
      BOOL re3 = [(id)[NSObject new]   isKindOfClass:[NSObject class]]; 
      BOOL re4 = [(id)[Person new]     isKindOfClass:[Person class]];
    
           NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
    
      BOOL re5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     
      BOOL re6 = [(id)[Person class]   isMemberOfClass:[Person class]];     
      BOOL re7 = [(id)[NSObject new]   isMemberOfClass:[NSObject class]];     
      BOOL re8 = [(id)[Person new]     isMemberOfClass:[Person class]];     
    
           NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
    

    你的答案是什么呢?不妨先停一停,多花些时间思考一下你的答案,然后再继续往下看。


    in thinking .......

    then

    go on


    此题主要考察两个方面的内容,① isasuperclass 的指向流程;② (+ / -) isKindOfClass(+ / - ) isMemberOfClass 的实现细节。

    re1re2 的第一个参数都是 ,所以是对 + (BOOL)isKindOfClass:(Class)cls 方法的考察。

    + (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    • 先看 re1
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
    

    传入的cls 为 NSobject, self 指向 NSobject,进入循环

    ①第一次循环: tcls 为 NSobject meta ,cls 为 NSobject ;执行判断条件if (tcls == cls) ,不相等;执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject meta 的父类 ,即 NSObject。进入第二次循环。

    ②第二次循环:此时 tcls 为 NSobject,cls 依然是 NSobject,执行判断条件 if (tcls == cls) 相等,return YES。

    所以 re1 的结果为 1

    • re2
    BOOL re2 = [(id)[Person class]   isKindOfClass:[Person class]]; 
    

    传入的cls为 NSobject,self指向 Person,进入循环

    ①第一次循环:tcls 为 Person meta,cls 为 Person类; 执行判断条件 if (tcls == cls) ,不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 NSobject metal。进入第二次循环。

    ②第二次循环: tcls 为 NSobject meta ,cls 为 Person类;不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 NSObject。进入第三次循环。

    ③第三次循环: tcls 为 NSobject ,cls 为 Person类;不相等,执行 tcls = tcls->superclass ,此时 tcls 指向 nil。不满足for循环执行条件 tcls。结束循环。

    所以 re2 的结果为 0

    + (BOOL)isKindOfClass:(Class)cls 考察的是:判断类或类的父类的类型是否是条件类

    re3re4 的第一个参数都是 对象,所以是对 -(BOOL)isKindOfClass:(Class)cls 方法的考察。

    - (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    • re3
    BOOL re3 = [(id)[NSObject new]   isKindOfClass:[NSObject class]]; 
    

    传入的cls 为 NSObject 类,self 指向 NSObject 的 实例对象

    ①第一次循环:tcls 指向 NSObject 类,cls 为 NSObject 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环。

    所以 re3 返回1。

    • re4
    BOOL re4 = [(id)[Person new]     isKindOfClass:[Person class]];
    

    传入的cls 为 Person 类,self 指向 Person 的 实例对象

    ①第一次循环:tcls 指向 Person 类,cls 为 Person 类,执行判断 if (tcls == cls) ,相等,return YES,结束循环。

    所以 re4 返回1。

    -(BOOL)isKindOfClass:(Class)cls 考察的是 判断对象的类 是否是条件类的子类。

    re5re6 的第一个参数都是 类,所以是对 + (BOOL)isMemberOfClass:(Class)cls 方法的考察。

    + (BOOL)isMemberOfClass:(Class)cls {
        return self->ISA() == cls;
    }
    
    • 先看 re5
    BOOL re5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];  
    

    传入的cls 为 NSObject, self 指向 NSObject

    self->ISA( ) ,self的 isa 指向 NSObject metaNSObject metaNSObject 不相等。

    所以 re5 的结果为 0

    • re6
     BOOL re6 = [(id)[Person class]   isMemberOfClass:[Person class]];  
    

    传入的cls 为 Person, self 指向 Person

    self->ISA( ) ,self的 isa 指向 Person metaPerson metaPerson 不相等。

    所以 re6 的结果为 0

    + (BOOL)isMemberOfClass:(Class)cls 考察的是类的元类 是否是条件类。

    re7re8 的第一个参数都是 对象 所以是对 -(BOOL)isMemberOfClass:(Class)cls 方法的考察。

    - (BOOL)isMemberOfClass:(Class)cls {
        return [self class] == cls;
    }
    
    • re7
    BOOL re7 = [(id)[NSObject new]   isMemberOfClass:[NSObject class]]; 
    

    传入的cls 为 NSObject, self 指向 NSObject 对象

    [self class] 为 NSObject 类 ;与 cls 相等。

    所以 re7 的结果为 1

    • re8
    BOOL re8 = [(id)[Person new]     isMemberOfClass:[Person class]]; 
    

    传入的cls 为 Person, self 指向 Person 对象

    [self class] 为 Person 类 ;与 cls 相等。

    所以 re8 的结果为 1

    -(BOOL)isMemberOfClass:(Class)cls 考察的是对象 是否是条件类的实例。

    1. 方法的存储示例

    创建 Person类 继承自 NSObject,实现方法如下:

    @implementation Person
    
    - (void)sayHello{
        NSLog(@"Person say : Hello!!!");
    }
    
    + (void)sayHappy{
        NSLog(@"Person say : Happy!!!");
    }
    
    @end
    

    分别用class_getInstanceMethodclass_getClassMethod 获取 objc_method

    下面输出的结果会是什么?

    void lgInstanceMethod_classToMetaclass(Class pClass){
        
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
        
        Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
        Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
    
        Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
        Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
        
        LGLog(@"%s - %p--%p--%p--%p",__func__,method1,method2,method3,method4);
    }
    

    在类的结构分析中,我们知道,对象的 实例方法 存储在 中,类方法 存储在 元类 之中。

    对于method1 :在 Person类 中查找- (void)sayHello 这样一个实例方法 ,显然是存在的。

    对于method2 :在 Person元类 中查找- (void)sayHello 这样一个实例方法 ,是不存在的。

    对于method3 :在 Person类 中查找+ (void)sayHappy 这样一个实例方法 ,是不存在的。

    对于method4 :在 Person元类 中查找+ (void)sayHappy 这样一个实例方法 ,是存在的。

    所以上述结果为:> - 0x1000031b0 -- 0x0 -- 0x0 -- 0x100003148

    继续看下面的输出结果:

    void lgClassMethod_classToMetaclass(Class pClass){
        
        const char *className = class_getName(pClass);
        Class metaClass = objc_getMetaClass(className);
        
        Method method1 = class_getClassMethod(pClass, @selector(sayHello));
        Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
    
        Method method3 = class_getClassMethod(pClass, @selector(sayHappy));  
        Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
        
        LGLog(@"%s-%p--%p--%p--%p",__func__,method1,method2,method3,method4);
    }
    

    我们直接输出结果: > - 0x0 -- 0x0 -- 0x100003148 -- 0x100003148

    对于method1 :在 Person类 中查找- (void)sayHello 这样一个类方法 ,不存在。

    对于method2 :在 Person元类 中查找- (void)sayHello 这样一个类方法 ,不存在。

    对于method3 :在 Person类 中查找+ (void)sayHappy 这样一个类方法 ,存在。

    对于method4 :在 Person元类 中查找+ (void)sayHappy 这样一个类方法 ,存在。

    对于method4,为什么 Person元类 中会 + (void)sayHappy 的类方法呢?

    我们跟踪到 class_getClassMethod

    Method class_getClassMethod(Class cls, SEL sel)
    {
        if (!cls  ||  !sel) return nil;
    
        return class_getInstanceMethod(cls->getMeta(), sel);
    }
    

    由底层实现可见,获取 类方法 就是获取 元类中的 实例方法,这是符合我们预期的。

    继续追踪到 cls->getMeta() 方法

    Class getMeta() {
            if (isMetaClass()) return (Class)this;
            else return this->ISA();
        }
    

    可见,在获取元类的时候,如果传入的就是元类,那么会返回本身;否则才会继续查找元类,而对于这个例子 Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));

    传入的已经是 Person 的元类, 那么会直接返回 Person 元类,而不是去找 Person元类的元类 。而 Person 元类中就是存在 + (void)sayHappy 这样一个类方法的,所以 method4 是有返回值的。

    为什么这样设计呢?

    因为在现实世界中,没人会向元类对象发送消息。

    1. 下面的代码输出什么内容?
    @implementation Son : Father
    - (id)init {
        self = [super init];
        if (self) {
            Class son = [self class];
            Class Father = [super class];
            NSLog(@"%@", NSStringFromClass(Son));
            NSLog(@"%@", NSStringFromClass(Father));
        }
        return self;
    }
    @end
    

    分析流程:首先我们将 Son.m 编译成 c++ 文件

    clang -rewrite-objc Son.m -o Son.cpp
    

    在c++中,看一下是如何实现的。

    // @implementation Son
    
    static id _I_Son_init(Son * self, SEL _cmd) {
        self = ((Son *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("init"));
        if (self) {
            
            Class son = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
            
            Class Father = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"));
            
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_m__6whbhm2540q9gqrsnmnkmkg40000gn_T_Son_58884b_mi_0, NSStringFromClass(son));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_m__6whbhm2540q9gqrsnmnkmkg40000gn_T_Son_58884b_mi_1, NSStringFromClass(Father));
        }
        return self;
    }
    // @end
    

    我们关注这两段代码:

    Class son = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
            
    Class Father = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"));
    

    objc中 super 是编译器标示符,并不像 self 一样是一个对象,遇到向 super 发的方法时会转译成 objc_msgSendSuper(...),而参数中的对象还是 self,于是从父类开始沿继承链寻找 - class 这个方法,最后在NSObject中找到(若无override),此时,[self class][super class] 已经等价了。

    所以 输出的结果 都是 "Son"。

    相关文章

      网友评论

        本文标题:runtime面试题分析

        本文链接:https://www.haomeiwen.com/subject/frbtyktx.html