美文网首页
《OC底层-经典面试1》isKindOfClass&isMemb

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

作者: 002and001 | 来源:发表于2020-11-04 20:31 被阅读0次

    前言

    今天我们结合前面所学的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。

    相关文章

      网友评论

          本文标题:《OC底层-经典面试1》isKindOfClass&isMemb

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