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

一, 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] 自己的类来进行对比,通过本文的分析,清楚的知道了各种比较原理,对以后开发过程中也明确了很多。本文只是自己理解,如果有不足之处请多多指教。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349