美文网首页
OC底层原理09-isKindOfClass和isMemberO

OC底层原理09-isKindOfClass和isMemberO

作者: Gomu_iOS | 来源:发表于2020-09-15 16:23 被阅读0次

    一、准备工作

    二、查看源码分析流程

    2.1 isKindOfClass 源码

    //: 实例方法
    - (BOOL)isKindOfClass:(Class)cls {
        //: 1、拿到调用者`self`的类,判断是否和cls相等
        //: 2、相等返回YES,不相等,则再去找`self`的类的父类,递归下去,直到nil跳出循环
        for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    //: 类方法
    + (BOOL)isKindOfClass:(Class)cls {
        //: 1、拿到调用者`self`的元类,判断是否和cls相等
        //: 2、相等返回YES,不相等,则再去找`self`的元类的父类,递归下去,直到nil跳出循环
        for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    • isKindOfClass实例方法是拿self去和cls做比较,相等则返回YES,不相等则再递归找父类是否与cls相等,直到找到nil,跳出循环返回NO
    • isKindOfClass类方法是拿self元类去和cls做比较,相等则返回YES,不相等则再递归找元类的父类是否与cls相等,直到找到nil,跳出循环返回NO

    举例:

    //: 调用isKindOfClass实例方法
    BOOL re1 = [[NSObject alloc] isKindOfClass:[NSObject class]];
    BOOL re2 = [[GomuPerson alloc] isKindOfClass:[GomuPerson class]];
    BOOL re3 = [[GomuPerson alloc] isKindOfClass:[NSObject class]];
    
    //: 调用isKindOfClass类方法
    BOOL re4 = [[NSObject class] isKindOfClass:[NSObject class]];
    BOOL re5 = [[GomuPerson class] isKindOfClass:[GomuPerson class]];
    BOOL re6 = [[GomuPerson class] isKindOfClass:[NSObject class]];
            
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n re5 :%hhd\n re6 :%hhd\n",re1,re2,re3,re4,re5,re6);
    
    //: 打印
    re1 :1
    re2 :1
    re3 :1
    re4 :1
    re5 :0
    re6 :1
    
    2.1.1 [[NSObject alloc] isKindOfClass:[NSObject class]]流程
    1. 拿到[NSObject alloc]class = NSObject
    2. 传入的cls=[NSObject class] = NSObject
    3. NSObject = NSObject,返回YES
    2.1.2 [[GomuPerson alloc] isKindOfClass:[GomuPerson class]]流程
    1. 拿到[GomuPerson alloc]class = GomuPerson
    2. 传入的cls=[GomuPerson class] = GomuPerson
    3. GomuPerson = GomuPerson,返回YES
    2.1.3 [[GomuPerson alloc] isKindOfClass:[NSObject class]]流程
    1. 拿到[GomuPerson alloc]class = GomuPerson
    2. 传入的cls=[NSObject class] =NSObject
    3. GomuPerson != NSObject,递归找GomuPerson的父类
    4. GomuPerson的父类 = NSObject
    5. NSObject = NSObject,返回YES
    2.1.4 [[NSObject class] isKindOfClass:[NSObject class]]流程
    1. 拿到[NSObject class]的元类 = NSObject元类,即根元类
    2. 传入的cls=[NSObject class] = NSObject
    3. NSObject元类 != NSObject,递归找根元类的父类
    4. 根元类的父类 = NSObject
    5. NSObject = NSObject,返回YES
    2.1.5 [[GomuPerson class] isKindOfClass:[GomuPerson class]]流程
    1. 拿到[GomuPerson class]的元类 = GomuPerson元类
    2. 传入的cls=[GomuPerson class] = GomuPerson
    3. GomuPerson元类 != GomuPerson,递归找GomuPerson元类的父类
    4. GomuPerson元类的父类 = 根元类
    5. 根元类 != GomuPerson,递归找根元类的父类
    6. 根元类的父类 = NSObject
    7. NSObject != GomuPerson,递归找NSObject的父类
    8. NSObject的父类 = nil
    9. nil != GomuPerson,跳出循环,返回NO
    2.1.6 [[GomuPerson class] isKindOfClass:[NSObject class]]流程
    1. 拿到[GomuPerson class]的元类 = GomuPerson元类
    2. 传入的cls=[NSObject class] = NSObject
    3. GomuPerson元类 != NSObject,递归找GomuPerson元类的父类
    4. GomuPerson元类的父类 = 根元类
    5. 根元类 != NSObject,递归找根元类的父类
    6. 根元类的父类 = NSObject
    7. NSObject = NSObject,返回YES
    2.1.7 流程图
    isKindOfClass流程图.png
    2.1.8 总结
    • isKindOfClass实例方法和类方法的区别在于,实例方法是拿对象的类去和cls比较,再进行递归找对象的类的父类进行比较,类方法则是拿类的元类去和cls比较,再进行递归找类的元类的父类进行比较
    • isKindOfClass实例方法中,即是判断当前调用方法的对象所属类是否继承cls或者与cls同一个类
    • isKindOfClass类方法中,即是判断当前调用方法的类所属元类是否继承cls或者与cls同一个类
    • 元类对象,那么isKindOfClass就很好理解了,不用区分是否是类/实例方法,他的作用就是判断当前对象(类对象)是否继承cls/与cls是同一个类

    2.2 isMemberOfClass 源码

    //: 实例方法
    - (BOOL)isMemberOfClass:(Class)cls {
        //: 拿到调用者`self`的类,判断是否和cls相等
        return [self class] == cls;
    }
    //: 类方法
    + (BOOL)isMemberOfClass:(Class)cls {
        //: 拿到调用者`self`的类的元类,判断是否和cls相等
        return self->ISA() == cls;
    }
    
    • isMemberOfClass实例方法是拿self的类,判断是否和cls相等
    • isMemberOfClass类方法是拿self的类的元类,判断是否和cls相等

    举例:

    //: 调用isMemberOfClass实例方法
    BOOL re1 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
    BOOL re2 = [[GomuPerson alloc] isMemberOfClass:[GomuPerson class]];
    BOOL re3 = [[GomuPerson alloc] isMemberOfClass:[NSObject class]];
    
    //: 调用isMemberOfClass类方法
    BOOL re4 = [[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL re5 = [[GomuPerson class] isMemberOfClass:[GomuPerson class]];
    BOOL re6 = [[GomuPerson class] isMemberOfClass:[NSObject class]];
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n re5 :%hhd\n re6 :%hhd\n",re1,re2,re3,re4,re5,re6);
    
    //: 打印
    re1 :1
    re2 :1
    re3 :0
    re4 :0
    re5 :0
    re6 :0
    
    2.2.1 [[NSObject alloc] isMemberOfClass:[NSObject class]]流程
    1. 拿到[NSObject alloc]class = NSObject
    2. 传入的cls=[NSObject class] = NSObject
    3. NSObject = NSObject,返回YES
    2.2.2 [[GomuPerson alloc] isMemberOfClass:[GomuPerson class]]流程
    1. 拿到[GomuPerson alloc]class = GomuPerson
    2. 传入的cls=[GomuPerson class] = GomuPerson
    3. GomuPerson = GomuPerson,返回YES
    2.2.3 [[GomuPerson alloc] isMemberOfClass:[NSObject class]]流程
    1. 拿到[GomuPerson alloc]class = GomuPerson
    2. 传入的cls=[GomuPerson class] = NSObject
    3. GomuPerson != NSObject,返回NO
    2.2.4 [[NSObject class] isMemberOfClass:[NSObject class]]流程
    1. 拿到[NSObject class]元类 = NSObject 元类
    2. 传入的cls=[NSObject class] = NSObject
    3. NSObject 元类 != NSObject,返回NO
    2.2.5 [[GomuPerson class] isMemberOfClass:[GomuPerson class]]流程
    1. 拿到[GomuPerson class]元类 = GomuPerson 元类
    2. 传入的cls=[GomuPerson class] = GomuPerson
    3. GomuPerson 元类 != GomuPerson,返回NO
    2.2.6 [[GomuPerson class] isMemberOfClass:[NSObject class]]流程
    1. 拿到[GomuPerson class]元类 = GomuPerson 元类
    2. 传入的cls=[NSObject class] = NSObject
    3. GomuPerson 元类 != NSObject,返回NO
    2.2.7 流程图
    isMemberOfClass流程图.png
    2.2.8 总结
    • isMemberOfClass实例方法中,即是判断当前调用方法的对象所属类是否与cls同一个类
    • isMemberOfClass类方法中,即是判断当前调用方法的类所属元类是否与cls同一个类
    • 元类对象,那么isMemberOfClass就很好理解了,不用区分是否是类/实例方法,他的作用就是判断当前对象(类对象)是否与cls是同一个类

    三、总结

    3.1 isKindOfClass的作用

    判断对象所属类/类对象所属元类是否继承cls/与cls相等

    3.2 isMemberOfClass的作用

    判断对象所属类/类对象所属元类是否与cls相等

    四、补充说明

    //: 经过断点调试,这两方法都不会走
    + (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    - (BOOL)isKindOfClass:(Class)cls {
        for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
            if (tcls == cls) return YES;
        }
        return NO;
    }
    
    //: 都会调用一个相同的方法
    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);
    }
    
    //: getIsa() 方法
    inline Class 
    objc_object::getIsa() 
    {
        if (fastpath(!isTaggedPointer())) return ISA();
    
        extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
        uintptr_t slot, ptr = (uintptr_t)this;
        Class cls;
    
        slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
        cls = objc_tag_classes[slot];
        if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
            slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
            cls = objc_tag_ext_classes[slot];
        }
        return cls;
    }
    
    //: ISA()方法
    inline Class 
    objc_object::ISA() 
    {
        ASSERT(!isTaggedPointer()); 
    #if SUPPORT_INDEXED_ISA
        if (isa.nonpointer) {
            uintptr_t slot = isa.indexcls;
            return classForIndex((unsigned)slot);
        }
        return (Class)isa.bits;
    #else
        return (Class)(isa.bits & ISA_MASK);
    #endif
    }
    
    • objc_opt_isKindOfClass 会调用 obj->getIsa()
    • obj->getIsa() 会调用ISA()
    • 进入ISA(),会返回(Class)(isa.bits & ISA_MASK)
    • (Class)(isa.bits & ISA_MASK) 就是把isa转成类
    • 所以当obj是实例对象的时候,obj->getIsa()等同于 [obj class],拿到对象的类。
    • obj是对象的时候,obj->getIsa()就是拿类的元类。

    isKindOfClass的实例方法和类方法都调同一个方法是没有问题的,逻辑是通的

    相关文章

      网友评论

          本文标题:OC底层原理09-isKindOfClass和isMemberO

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