《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。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。