前言
探索目的
本篇文章对isKindOfClass
、isMemberOfClass
的探索,是为了加深对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 类方法底层原理
- 底层源码
+ (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
}
- 了解
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)
返回的结果必定是当前类对象所对应的元类。
- 分析
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 对象方法底层原理
- 底层源码
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
[self class]
直接该对象当前的类。
- 分析
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 类方法底层原理
- 底层源码
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
结合
isKindOfClass类方法
,区别点是只找当前,不走继承链。
- 分析
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 对象方法底层原理
- 底层源码
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
- 分析
直接获取对象的类。
分析
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
的指向和继承链关系图,一切的解释在这幅图里面都可以找到。
学习之路,砥砺前行
不足之处可以在评论区指出