OC底层原理09-isKindOfClass和isMemberO

2020-09-15  本文已影响0人  Gomu_iOS

一、准备工作

二、查看源码分析流程

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
}

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

上一篇下一篇

猜你喜欢

热点阅读