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

2020-09-15  本文已影响0人  携YOU手同行

一, 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指针的指向,元类中存的是该类的类方法,在元类中以实例方法的形式存在,类的归属信息来自元类

结果分析
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,进行递归。

结果分析

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


图片.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的定义,我们从代码可以看到存在两种定义,

+ (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,

- (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走位图能清楚的知道,

结果分析
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的定义,我们从代码可以看到存在两种定义,

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

原理如下

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

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

原理如下

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

结果分析

三、总结

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

上一篇下一篇

猜你喜欢

热点阅读