《OC底层-经典面试1》isKindOfClass&isMemberOfClass

前言

今天我们结合前面所学的OC底层来对经典的面试题进行分析,将学习成果运用到实践中。

目录

目录.png

1、简介

在日常开发中我们经常用到isKindOfClass或者isMemberOfClass的实例方法或者类方法,其是由RunTime提供封装的Api,但是我们是否深入理解和掌握了呢?

1.1、经典面试题

看看下面的代码输出什么

// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end

// Person.m
#import "Person.h"
@implementation Person
@end

// main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
        BOOL re1 = [[NSObject alloc] isKindOfClass:[NSObject class]];
        BOOL re2 = [[Person alloc] isKindOfClass:[Person class]];
        BOOL re3 = [[NSObject class] isKindOfClass:[NSObject class]];
        BOOL re4 = [[Person class] isKindOfClass:[Person class]];
        NSLog(@" re1 :%d\n re2 :%d\n re3 :%d\n re4 :%d\n",re1,re2,re3,re4);

        BOOL re5 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
        BOOL re6 = [[Person alloc] isMemberOfClass:[Person class]];
        BOOL re7 = [[NSObject class] isMemberOfClass:[NSObject class]];
        BOOL re8 = [[Person class] isMemberOfClass:[Person class]];
        NSLog(@" re5 :%d\n re6 :%d\n re7 :%d\n re8 :%d\n",re5,re6,re7,re8);
    }
    return 0;

上面是关于isKindOfClass 和 isMemberOfClass的经典分析题目,输出结果你答对了么?

 re1 :1
 re2 :1
 re3 :1
 re4 :0

 re5 :1
 re6 :1
 re7 :0
 re8 :0

上面的题目实际上考察了OC底层的isa、superclass走位以及isKindOfClass&isMemberOfClass的源码如何实现,
让我们回顾一下isa和superclass走位图,对源码分析的时候需要用到

isa&superclass.png

有了前面的《OC底层》的学习的知识,并结合上图以及源码,可以很好的分析了。

2、isKindOfClass

2.1、- (BOOL)isKindOfClass:

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

- (Class)class {
    return object_getClass(self);
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

根据源码和isa走位图可以知道,- (BOOL)isKindOfClass:用于判断当前实例所属的类是否==cls,不相等则沿着superclass走位一直遍历比较到NSObject类,路径上有相等的则返回false,遍历结束均不相等则返回false
其对比路径如下图红色部分:

image.png

2.1.1、[[NSObject alloc] isKindOfClass:[NSObject class]]
BOOL re1 = [[NSObject alloc] isKindOfClass:[NSObject class]];

比较过程如下图所示

re5.png

tcls初始值为[[NObject alloc] class],即[NSObject class],因此re1==1;

2.1.2、[[Person alloc] isKindOfClass:[Person class]]
BOOL re2 = [[Person alloc] isKindOfClass:[Person class]];

re2.png

[Person alloc]的isa指向Person类,即[Person class],因此re2==1;

2.2、+ (BOOL)isKindOfClass

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

根据源码和isa走位图可以知道,+ (BOOL)isKindOfClass:用于判断当前类的元类是否==cls,不相等则从该类的元类开始,沿着superclass一直遍历比较到NSObject类是否==cls,有相等的则返回true,遍历结束均不相等返回false。按照如下图标红部分进行比较:

+ (BOOL)isKindOfClass:

2.2.1、 [[NSObject class] isKindOfClass:[NSObject class]]

BOOL re3 = [[NSObject class] isKindOfClass:[NSObject class]];
re3.png

tcls初始值为[NSObject class]->ISA(),即NSObject元类,NSObject元类的superclass又指向[NSObject class],因此re3==1;

2.2.2、 [[Person class] isKindOfClass:[Person class]]

BOOL re4 = [[Person class] isKindOfClass:[Person class]];

re4.png

从上图可以看到,tcls初始值为[Person class]->ISA(),即Person元类,一直比较遍历到NSObject类,均不等于[Person class],因此re4==0;

3、isMemberOfClass

3.1、- (BOOL)isMemberOfClass:

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

比较当前实例的类是否==cls。

3.1.1、[[NSObject alloc] isMemberOfClass:[NSObject class]]
BOOL re5 = [[NSObject alloc] isMemberOfClass:[NSObject class]];

[self class] = [[NSObject alloc] class] == [NSObject class],
cls=[NSObject class],所以 [self class] == cls == 1;

3.1.2、[[Person alloc] isMemberOfClass:[Person class]];
BOOL re6 = [[Person alloc] isMemberOfClass:[Person class]];

[self class]= [[Person alloc] class] == [Person class] == 1;
cls=[Person class],所以 [self class] == cls == 1;

3.2、+ (BOOL)isMemberOfClass:

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

比较当前类的元类是==cls

3.2.1、[[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re7 = [[NSObject class] isMemberOfClass:[NSObject class]];

根据isa走位图,[[NSObject class]->ISA()为NSObject的元类,不等于[[NSObject class],因此re6==0;

3.2.2、[[Person class] isMemberOfClass:[Person class]];
BOOL re8 = [[Person class] isMemberOfClass:[Person class]];

根据isa走位图,[[Person class]->ISA()为Person元类,不等于[[Person class],因此re8==0;

4、注意

实际运行源码调试的时候会发现,isMemberOfClass会走到源码处,但是isKindOfClass不会,isKindOfClass的示例方法和类方法实际上调用的都是** objc_opt_isKindOfClass**

// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
    if (slowpath(!obj)) return NO;
    Class cls = obj->getIsa();
    if (fastpath(!cls->hasCustomCore())) {
        for (Class tcls = cls; tcls; tcls = tcls->superclass) {
            if (tcls == otherClass) return YES;
        }
        return NO;
    }
#endif
    return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}

原因在于llvm中编译时对其进行了优化处理。

5、总结

  • - (BOOL)isKindOfClass:用于判断当前实例所属的类是否==cls,不相等则沿着superclass走位一直遍历比较到NSObject类,路径上有相等的则返回false,遍历结束均不相等则返回false
  • + (BOOL)isKindOfClass:用于判断当前类的元类是否==cls,不相等则从该类的元类开始,沿着superclass一直遍历比较到NSObject类是否==cls,有相等的则返回true,遍历结束均不相等返回false。
  • -(BOOL)isMemberOfClass:比较当前实例的类是否==cls。
  • +(BOOL)isMemberOfClass:比较当前类的元类是否==cls。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353