iOS 底层探索篇 —— isKindOfClass、isMemberOfClass探索

453 阅读5分钟

前言

探索目的

本篇文章对isKindOfClassisMemberOfClass的探索,是为了加深对isa指向分析的理解。

代码分析

1. 代码

BOOL b1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL b2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL b3 = [[XDPerson class] isKindOfClass:[XDPerson class]];
BOOL b4 = [[XDPerson class] isMemberOfClass:[XDPerson class]];

BOOL b5 = [[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL b6 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL b7 = [[XDPerson alloc] isKindOfClass:[XDPerson class]];
BOOL b8 = [[XDPerson alloc] isMemberOfClass:[XDPerson class]];
   
NSLog(@"b1 - %d",b1);
NSLog(@"b2 - %d",b2);
NSLog(@"b3 - %d",b3);
NSLog(@"b4 - %d",b4);
NSLog(@"b5 - %d",b5);
NSLog(@"b6 - %d",b6);
NSLog(@"b7 - %d",b7);
NSLog(@"b8 - %d",b8);

看到以上的代码,不知道读者是否能够第一时间能全部回答正确,笔者在没有深入了解isa的指向之前,全部是靠蒙的。没有了解过isa的读者,可以去看看iOS 底层探索篇 —— isa的初始化&指向分析这篇文章。

2. 输出结果

不知道读者分析对了没有,如果全部正确,并且知道原因,恭喜你可以不用看我下面的分析了。若你全部是蒙的,不知其所以然,那笔者认为你有必要去了解一下我下面的分析了。

isKindOfClass分析

1. 类方法底层分析

1.1 类方法例子

BOOL b1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL b3 = [[XDPerson class] isKindOfClass:[XDPerson class]];

1.2 类方法底层原理

  1. 底层源码
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

说白了我们只要是正确的理解object_getClass((id)self)这段代码的含义,下面笔者全部把源码代码贴上来。

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

inline Class 
objc_object::getIsa() 
{
    if (!isTaggedPointer()) return ISA();

    uintptr_t ptr = (uintptr_t)this;
    if (isExtTaggedPointer()) {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        return objc_tag_ext_classes[slot];
    } else {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
        return objc_tag_classes[slot];
    }
}

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
}
  1. 了解isTaggedPointer()函数
inline bool 
objc_object::isTaggedPointer() 
{
    return _objc_isTaggedPointer(this);
}
define _OBJC_TAG_MASK 1UL

static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

``

我们先不去讲解taggedPointer是做什么用的,我们单存的去分析一个类的内存地址必定是类似0x00232344这样的值,强转long类型之后,通过$运算,而且$上的值还是1,那么必然结果不会等于1。

SUPPORT_INDEXED_ISA

#if __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && !__LP64__)
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif

在iOS手机端的平台上 宏定义值为0。

通过第二步的分析,代码的走向ISA() -> (Class)(isa.bits & ISA_MASK)

iOS 底层探索篇 —— isa的初始化&指向分析这篇文章中,我们已经了解到了(不清楚的可以去看看这篇文章)

  • 当前的objc若是实例对象,那么(isa.bits & ISA_MASK)返回的结果必定是当前实例对象的所对应的
  • 当前的objc若是类对象,那么(isa.bits & ISA_MASK)返回的结果必定是当前类对象所对应的元类
  1. 分析
  • isKindOfClass类方法底层函数实际上就是一个for循环,通过继承链往上查找
  • object_getClass((id)self),当前objc是一个类对象,那么获取的类就是当前类的元类。

分析

BOOL b1 = [[NSObject class] isKindOfClass:[NSObject class]];;
  • 第一步查找获取的是NSObject元类,匹配NSObject类,失败。
  • 进入循环根据NSObject元类,查找superclass,获取NSObject类,匹配NSObject类,成功。

分析

BOOL b3 = [[XDPerson class] isKindOfClass:[XDPerson class]];
  • 第一步查找获取的是XDPerson元类,匹配XDPerson类,失败。
  • 进入循环根据XDPerson元类,查找superclass,获取NSObject元类,匹配XDPerson类,失败。
  • 进入循环根据NSObject元类,查找superclass,获取NSObject类,匹配XDPerson类,失败。
  • 进入循环根据NSObject类,查找superclass,获取nil,跳出循环,匹配失败。

2. 对象方法底层分析

2.1 对象方法例子

BOOL b5 = [[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL b7 = [[XDPerson alloc] isKindOfClass:[XDPerson class]];

2.2 对象方法底层原理

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

[self class]直接该对象当前的类。

  1. 分析
  • isKindOfClass对象方法底层函数实际上就是一个for循环,通过继承链往上查找。
  • [self class]直接获取当前类。

分析

BOOL b5 = [[NSObject alloc] isKindOfClass:[NSObject class]];
  • 第一步获取该对象的类即NSObject类,匹配NSObject,成功。

分析

BOOL b7 = [[XDPerson alloc] isKindOfClass:[XDPerson class]];
  • 第一步获取该对象的类即XDPerson类,匹配XDPerson,成功。

isMemberOfClass分析

1. 类方法底层分析

1.1 类方法例子

BOOL b2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL b4 = [[XDPerson class] isMemberOfClass:[XDPerson class]];

1.2 类方法底层原理

  1. 底层源码
+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

结合isKindOfClass类方法,区别点是只找当前,不走继承链。

  1. 分析

object_getClass((id)self)获取元类。

分析

BOOL b2 = [[NSObject class] isMemberOfClass:[NSObject class]];

获取的是NSObject元类,匹配NSObject类,失败。

分析

BOOL b4 = [[XDPerson class] isMemberOfClass:[XDPerson class]];

获取的是XDPerson元类,匹配XDPerson类,失败。

2. 对象方法底层分析

2.1 对象方法例子

BOOL b6 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL b8 = [[XDPerson alloc] isMemberOfClass:[XDPerson class]];

2.2 对象方法底层原理

  1. 底层源码
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
  1. 分析

直接获取对象的类。

分析

BOOL b6 = [[NSObject alloc] isMemberOfClass:[NSObject class]];

获取到NSObject类,匹配NSObject类,成功。

分析

BOOL b8 = [[XDPerson alloc] isMemberOfClass:[XDPerson class]];

获取到XDPerson类,匹配XDPerson类,成功。

总结

1. isKindOfClass总结

isKindOfClass沿着继承链查询。

  • +类方法,获取元类,沿着元类的继承链查找。
  • -对象方法,获取类,沿着类的继承链查找。

2. isMemberOfClass总结

isMemberOfClass只找到当前。

  • +类方法,获取元类。
  • -对象方法,获取类。

最后附上isa的指向和继承链关系图,一切的解释在这幅图里面都可以找到。

学习之路,砥砺前行

不足之处可以在评论区指出