美文网首页专注iOS开发的小渣渣
经典面试题分析(一)

经典面试题分析(一)

作者: Henry________ | 来源:发表于2020-09-17 16:23 被阅读0次

    几道isa相关的经典面试题以及分析

    前期准备:

    @interface HRTest : NSObject
        - (void)sayHello;
        + (void)sayHappy;
    @end
    

    方法归属相关测试

    第一道

    HRTest *test = [HRTest alloc];
            Class pClass     = object_getClass(test);
    
    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));
        
        NSLog(@"%p-%p-%p-%p",method1,method2,method3,method4);
    

    输出结果:

    • class_getInstanceMethod获取当前类的实例方法
    • 实例方法存在类中,类方法存在元类中

    第二道

    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));
        
        NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
    

    输出结果:


    • class_getClassMethod获取当前类的类方法
    • 前三个都很好理解:sayHello是实例方法,不可能找到,sayHappy是类方法,自然存在于当前类中。可是当前类的元类中不可能存在该类方法,查看该方法实现的源码。
    class_getClassMethod的方法实现
    Method class_getClassMethod(Class cls, SEL sel)
    {
        if (!cls  ||  !sel) return nil;
    
        return class_getInstanceMethod(cls->getMeta(), sel);
    }
    
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }
    
    • 看到这里有木有忽然开朗,其实类的类方法在底层中的存在形式:元类中的实例方法。(c、c++中并没有实例、类方法)
    • 第四步判断存在的真正原因是:碰到元类会直接返回当前元类,并不会继续查找向上查找(元类的isa指向根源类)
    • metaClass已经是元类了,无需找到根源类了;第三步、第四步等价。
    小总结

    实例方法存在类中,类方法存在元类中。

    第三道

    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
        IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
        IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
        IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
    
    • 第二步、第三步中为什么会存在呢?而且地址是相同的!还是先看一下源码是如何实现的.
    IMP class_getMethodImplementation(Class cls, SEL sel)
    {
        IMP imp;
        if (!cls  ||  !sel) return nil;
        imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);   //先找缓存cache_t,然后去cls->data()->methods()中查找
        if (!imp) {
            return _objc_msgForward;    //找不到回到这里
        }
        return imp;
    }
    
    • 通过源码发现方法查找先是从cache_t中找,然后去cls->data()->methods()中查找,最后会进入_objc_msgForward流程.
    0x100001d00-0x7fff6c018580-0x7fff6c018580-0x100001d30
    
    (lldb) p/x imp1
    (IMP) $0 = 0x0000000100001d00 (002-类方法归属分析`-[LGPerson sayHello] at LGPerson.m:13)
    
    (lldb) p/x imp4
    (IMP) $1 = 0x0000000100001d30 (002-类方法归属分析`+[LGPerson sayHappy] at LGPerson.m:17)
    
    (lldb) p/x imp2
    (IMP) $2 = 0x00007fff6c018580 (libobjc.A.dylib`_objc_msgForward)
    
    • 通过打印得知,0x00007fff6c018580不该存在的这个地址是:_objc_msgForward流程!!并不是找到真的方法!!!

    第四道

    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];   
            BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; 
            BOOL re3 = [(id)[HRTest class] isKindOfClass:[HRTest class]];  
            BOOL re4 = [(id)[HRTest class] isMemberOfClass:[HRTest class]];
    

    输出结果:


    • +isKindOfClass类方法:
    + (BOOL)isKindOfClass:(Class)cls {
        //使用调用者的isa和当前入参对比。
        for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    

    循环:
    第一步:NSObject的isa -> 根元类
    第二步:根元类的superclass -> NSObject
    所以第一个相等;HRTest的isa->superclass指向根源类并不相等

    • +isMemberOfClass类方法:
    + (BOOL)isMemberOfClass:(Class)cls {
        return self->ISA() == cls;
    }
    

    拿当前类的isa指向和参数做对比,相同的类一定不相等.

    第五道

    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
            BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
            BOOL re7 = [(id)[HRTest alloc] isKindOfClass:[LGPerson class]];       //
            BOOL re8 = [(id)[HRTest alloc] isMemberOfClass:[LGPerson class]]; 
    
    • -isKindOfClass
    - (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    • -isMemberOfClass
    - (BOOL)isMemberOfClass:(Class)cls {
        return [self class] == cls;
    }
    
    

    这道题很简单,现实面试应该是碰不到这样的题目。

    相关文章

      网友评论

        本文标题:经典面试题分析(一)

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