美文网首页
iOS中经典面试题的分析和总结

iOS中经典面试题的分析和总结

作者: 携YOU手同行 | 来源:发表于2020-09-15 18:47 被阅读0次

    一, class_getClassMethod 与 class_getInstanceMethod 的分析,

    首先我们先在项目中定义一个类LGPerson 此类继承自NSObject,我们给这个类添加两个方法,一个对象方法,一个类方法,

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

    然后我们在main.m中进行初始化这个类的示例对象。然后在用objc_getClass()来获取类的信息。我们此处得到类信息后,

    封装了一下三个方法的实现,然后分析一下每个方法打印的内容是什么?为什么是这样的结果?

    1,class_getInstanceMethod的分析
    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(@"method1是%p------method2是%p------method3是%p-------method4是%p",method1,method2,method3,method4);
    }
    

    method1 到method4 各打印什么内容,为什么?

    我们可以看到打印的内容是


    图片.png
    原理介绍

    接下来我们具体的分析相关的的结果为什么是这样,首先我们看class_getInstanceMethod的定义如下

    Method class_getInstanceMethod(Class cls, SEL sel)
    {
        if (!cls  ||  !sel) return nil;
    #warning fixme build and search caches
        lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
    #warning fixme build and search caches
        return _class_getMethod(cls, sel);
    }
    

    从字面量我们就知道这是通过该类查找方法的编号,通过Sel来再类的方法列表中查找,如果找到就直接返回,如果没找到就进行lookUpImpOrForward递归的查找,

    lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
    

    通过查找原理我们知道了相关的查找流程,那么接下来的结果应该就是很简单了;我们都知道通过相关isa指针的指向,元类中存的是该类的类方法,在元类中以实例方法的形式存在,类的归属信息来自元类

    结果分析
    • method1 :通过@selector(sayHello)去类中查找相关的实例方法,本身次方法就是在类中存 在的是实例,所以肯定能找到,所以打印结果是0x1000031b0

    • method2: 通过@selector(sayHello)去元类 中查找相关实例,因为我们都知道元类存储的是类的类方法,并以实例的形式存在,所以取元类中查找注定失败,所以结果是 0x0

    • method3: 通过@selector(sayHappy)去类中查找实例方法,因为在类的声明中sayHappy是类方法而非实例方法,所以肯定查找失败,所以结果是0x0

    • method4: 通过@selector(sayHappy)去元类 中查找实例方法,根据类方法在元类中是以实例方法的形式存储,所以这个查找结果是必定存在的,所以结果是0x100003148

    2,class_getClassMethod的分析

    方法代码如下:

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

    问题

    method1 到method4 各打印什么内容,为什么?

    我们看到程序打印的结果是


    图片.png
    原理分析

    首先我们看class_getClassMethod的定义,如下

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

    虽然是查找类的方法,同时的实质也是通过元类查找存储在元类中的实例方法。从class_getInstanceMethod(cls->getMeta(), sel);我们就能清楚的知道;

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

    首先判断是否是元类,如果是就直接返回,否则在查找相关的类的ISA,进行递归。

    结果分析
    • method1 :通过@selector(sayHello)去类中查找相关的类方法,本身此方法就是在类中存在的是实例的形式存储,所以肯定不能找到,所以打印结果是0x0

    • method2: 通过@selector(sayHello)去元类 中查找类方法,因为我们都知道元类存储的是类的类方法,并以实例的形式存在,所以取元类中查找注定失败,所以结果是 0x0

    • method3: 通过@selector(sayHappy)去类中查找类方法,因为在类的声明中sayHappy就是是类方法 所以肯定查找成功,所以结果是0x100003148

    • method4: 通过@selector(sayHappy)去元类 中查找类方法,

      根据类方法在元类中是以实例方法的形式存储,那么这个查找结果是为什么还能存在呢?

    解答:上边的原理我们已经知道了,


    图片.png

    如果是元类直接返回元类的实例方法,所以该类方法在元类中以实例的形式存在,所以肯定是能存在的,所以打印结果是 0x100003148

    二,isKindOfClass 与 isMemberOfClass 的分析和学习

    在开发中我们经常使用的isKindOfClassisMemberOfClass,但是让我们具体的说出二者有什么区别我们可能还真的不太好说清楚,二者的底层代码实现更是无从下手。接下来我们就来深入的分析一下相关的二者区别和源码解析。

    我们在项中也创建一个LGPerson 继承与NSObject;接下来我们来分析如下代码的结果,简单的说一下为什么结果是这样的

    1,isKindOfClass的分析
            BOOL result1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
            BOOL result2 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
            BOOL result3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
            BOOL result4 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];//
               //
            NSLog(@"\n result1 :%hhd\n result2 :%hhd\n result3 :%hhd\n result4 :%hhd\n",result1,result2,result3,result4);
    

    打印的结果是


    图片.png
    原理分析

    我们进入isKindOfClass的定义,我们从代码可以看到存在两种定义,

    • 1 是类方法
    + (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    

    通过类方法+ (BOOL)isKindOfClass:(Class)cls`的定义,我们不难发现,其原理是:

    1 、首先通过当前的类得到当前类的元类
    2、判断元类是否存在,如果存在,和当前的类进行对比,如果相等就返回YES;
    3、如果元类不存在,再递归向父类查找,找到根元类,和当前的类进行比较,如果相等返回YES,否则返回NO,

    • 2 是对象方法的定义
    - (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    

    通过类方法- (BOOL)isKindOfClass:(Class)cls的定义,我们不难发现,其原理是:

    1、首先根据当前的类,得到当前类的class 为tcls
    2、判断当前类的class和类是否相等,如果相等则返回YES
    3,如果当前类的class不存在,则递归找到父类,再来进行对比,如果相等返回YES,否则返回NO,

    此处我们在附上一副经典的ISA走位图,就再熟悉不过了


    isa走位图.png

    我们通过isa走位图能清楚的知道,

    • 1元类的父类是继承父类元类
    • 2 继承父类元类的父类是根元类,
    • 3根元类的父类又是NSObject
    • 4NSObject的isa 又指向根元类
    • 5 根元类的isa 有指向了自己
    结果分析
    • result1:为+ (BOOL)isKindOfClass:,是通过isa来判断,根据 NSObject的isa 又指向根元类
      根元类的isa 有指向了自己,所以二者的ISA最后肯定是相等的;所以输出的结果是 1

    • result2:为- (BOOL)isKindOfClass:, 是通过class来判断,因为所以通过类实例的对象和[self class]都是同一个对象,所以他自己创建的类和他自己永远是相等的 所以输出的结果是 1

    • result3:为+ (BOOL)isKindOfClass:,是通过isa来判断,因为LGPerson的元类指向根元类,和LGPerson这个类是不相等,所以打印结果是 0;

    • result4:为- (BOOL)isKindOfClass:, 是通过class来判断,因为所以通过类实例的对象和[self class]都是同一个对象,所以他自己创建的类和他自己永远是相等的 所以输出的结果是1

    2、isMemberOfClass的分析

    代码如下

           BOOL result5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
            BOOL result6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
            BOOL result7 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
            BOOL result8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];     //
            NSLog(@" result5 :%hhd\n result6 :%hhd\n result7 :%hhd\n result8 :%hhd\n",result5,result6,result7,result8);
    

    打印结果如下


    图片.png
    原理分析,

    我们进入isMemberOfClass的定义,我们从代码可以看到存在两种定义,

    • 1,类的定义如下
    + (BOOL)isMemberOfClass:(Class)cls {
        return self->ISA() == cls;
    }
    

    原理如下

    判断当前类的ISA是否和类相等。如果相等则返回YES 否则返回No

    • 2、对象的定义如下
    - (BOOL)isMemberOfClass:(Class)cls {
        return [self class] == cls;
    }
    

    原理如下

    判断当前类的class是否和实例对象相等。如果相等则返回YES 否则返回No

    结果分析
    • result5:为+ (BOOL)isMemberOfClass:,是通过isa来判断,根据 NSObjectisa指向根元类,所以根元类NSObject类不相等,所以结果是 0

    • result6:为- (BOOL)isMemberOfClass:, 是通过class来判断,因为所以通过类实例的对象和[self class]都是同一个对象,所以他自己创建的类和他自己永远是相等的 所以输出的结果是 1

    • result7:为+i(BOOL)isMemberOfClass:,是通过isa来判断,因为LGPerson的元类指向根元类,和LGPerson这个类是不相等,所以打印结果是 0;

    • result8:为- (BOOL)isMemberOfClass:, 是通过class来判断,因为所以通过类实例的对象和[self class]都是同一个对象,所以他自己创建的类和他自己永远是相等的 所以输出的结果是1

    三、总结

    通过本文,我们清楚的知道如果是类的比较对象,不管是+ (BOOL)isMemberOfClass:(Class)cls 还是 + (BOOL)isKindOfClass:,都是通过类对象的isa来进行判断,如果是对象的对比,不管是 (BOOL)isMemberOfClass:还是 - (BOOL)isKindOfClass:,都是通过[self class] 自己的类来进行对比,通过本文的分析,清楚的知道了各种比较原理,对以后开发过程中也明确了很多。本文只是自己理解,如果有不足之处请多多指教。

    相关文章

      网友评论

          本文标题:iOS中经典面试题的分析和总结

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