关系总揽
⬇️首先你需要知道有这张图的存在,具体关系在下面在一步一步来验证
1、对象isa指向
基础概念
实例对象(objc)、类对象(class)、元类对象(meta)、根元类(root meta)
他们之间的关系是怎么样的呢?
创建一个Student类
其层级结构为:
Student:Person
Person:NSOject
先来看一段代码
Student *obj = [Student alloc];
Class class1 = [obj class];
Class class2 = [Student class];
Class class3 = object_getClass(obj); // 需要导入 头文件
Class class4 = NSClassFromString(@"Student");
NSLog(@"\
class1 %p-%@\n\
class2 %p-%@\n\
class3 %p-%@\n\
class4 %p-%@",class1,class1,
class2,class2,
class3,class3,
class4,class4);
其打印结果为:
class1 0x1043f0858-Student
class2 0x1043f0858-Student
class3 0x1043f0858-Student
class4 0x1043f0858-Student
首先要先明白,这些信息代表的是什么意思
class1、class2、class3、class4这四个值都是表示 类对象
知识点:
整个程序中,类对象只存在一份
类对象就是类的对象
上面的代码中Student
是类,上面的4个class就是类对象
上面代码中 [obj class];和[Student class]; 虽然都调用的是class方法,但是却不是同一个方法: [obj class]中的class是实例方法 /静态方法,而[Student class] 中的class是类方法
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
从源码中可以看到 类方法 返回的是自己,而实例方法返回的是对象的isa
》》》》》》
如何知道isa指向的是谁呢?在探究这个问题之前,我们需要知道一个问题:通过object_getClass(objc)得到的isa内存地址是优化过的
,而不是其真实地址。具体可以在源码中看出来
Class object_getClass(id obj)
{
if (obj) return obj->getIsa(); //点进去
else return Nil;
}
inline Class
objc_object::getIsa()
{
if (!isTaggedPointer()) return ISA(); // 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);
// ⚠️ 这里为什么要进行&运算? 因为 isa是联合体,第 3-35位用来表示 class
// ⚠️ ISA_MASK 转换成二进制 就是 第3-35位全为0的16进制数据 相当于 只读取 3-35位数据
#endif
}
SUPPORT_INDEXED_ISA :
表示 isa_t 中存放的 Class 信息是 Class 的地址,还是一个索引(根据该索引可在类信息表中查找该类结构地址)。
#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
# define SUPPORT_INDEXED_ISA 1
#else
# define SUPPORT_INDEXED_ISA 0
#endif
目前接触到的iOS的设备上 SUPPORT_INDEXED_ISA 为0
那么通过x指令的地址得到isa的真实地址与object_getClass(objc)的地址相对比的话,需要在地址的基础上 & 上 ISA_MASK
《《《《《《
获得isa地址 方法一
我们先执行一下代码,并将断点打在Class class = object_getClass(objc)这一行
- (void)printObjInfo:(id)objc{
NSLog(@"*********************************************");
NSLog(@"\n实例对象地址 - %p",objc);
Class class = object_getClass(objc);
NSLog(@"类对象 - %p %@",class,class);
class = object_getClass(class);
NSLog(@"元类对象 - %p %@",class,class);
class = object_getClass(class);
NSLog(@"根元类对象 - %p %@",class,class);
class = object_getClass(class);
NSLog(@"根根元类对象 - %p %@",class,class);
NSLog(@"*********************************************");
}
到了断点的时候,在控制台输入指令x objc
或者 x 0x6000034c5ee0
或者 Debug -> Debug Workflow -> View Memory
我们得到isa地址为 0x01028a1840,继续输入指令
p/x 0x01028a1840 & 0x00007ffffffffff8
p/x表示按照16进制打印地址 0x00007ffffffffff8 就是上面提到源码中的isa优化偏移
得到 0x00000001028a1840 这样的一个地址
这个地址就是优化过的isa地址。
获得isa地址方法2
直接在控制台输出
p/x obj->isa
得到的地址结果是 (Class) $0 = 0x00000001028a1840 Student
断点继续往下走一步,打印出内容:类对象 - 0x1028a1840 Student
由此可见: 实例对象的isa就是类对象
用同样的方法,我们可以一步一步的验证 类对象的isa指向、元类对象的isa指向、根元类的isa指向
最后我们得到一下关系:
实例对象isa -> 类对象
类对象isa -> 元类对象
元类对象isa -> 根元类
根源类isa -> 自己
最后的打印结果:
*********************************************
实例对象地址 - 0x6000034c5ee0
类对象 - 0x1028a1840 Student
元类对象 - 0x10058f818 Student
根元类对象 - 0x7fff89aec158 NSObject
根根元类对象 - 0x7fff89aec158 NSObject
*********************************************
此结果验证了开篇图中的
Instance of Subclass ->
Subclass(Class) ->
Subclass(meta) - >
Root class(meta) -> Root class(meta)
以上是对有继承关系的类进行打印,那么对没有继承关系的答应结果会是一样的吗? 用NSobjec来做打印测试得到如下结果:
NSObject *obj = [NSObject new];
打印结果
实例对象地址 - 0x600003dfc340
类对象 - 0x7fff89aec180 NSObject
元类对象 - 0x7fff89aec158 NSObject
根元类对象 - 0x7fff89aec158 NSObject
根根元类对象 - 0x7fff89aec158 NSObject
此结果验证了开篇图中的
Instance of Root class ->
Root class(Class) ->
Root class(meta) - > Root class(meta)
同理,也可以实例化一个Person对象进行打印,最后我们可以验证文章开始的那张图的isa走向关系
2、父类指向
先看源码:
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
superclass类方法和实例方法,返回的结果都是 类的 superclass数据 (这里涉及到类的结构,在另一篇文章中有讲到)
对象的父类走向
Student *obj = [Student new]; //实例化Student对象
[self printSuperClass:obj];
- (void)printSuperClass:(id)objc
{
NSLog(@"*********************************************");
Class class = [objc superclass];
NSLog(@"一父类 - %p %@ 其父类的类对象-%p",class,class,[Person class]);
class = [class superclass];
NSLog(@"二父类 - %p %@ 其父类的类对象-%p",class,class, [NSObject class]);
class = [class superclass];
NSLog(@"三父类 - %p %@",class,class);
class = [class superclass];
NSLog(@"四父类 - %p %@",class,class);
class = [class superclass];
NSLog(@"五父类 - %p %@",class,class);
NSLog(@"*********************************************");
}
打印结果
*********************************************
一父类 - 0x10bc33880 Person 其父类的类对象-0x10bc33880
二父类 - 0x7fff89aec180 NSObject 其父类的类对象-0x7fff89aec180
三父类 - 0x0 (null)
四父类 - 0x0 (null)
五父类 - 0x0 (null)
*********************************************
[objc superclass] 按照源码看可以拆解成 [[objc class] superclass] 也就是 [[Student class] superclass]
由此打印可以验证文章开头的那张图的对象的父类走向
Subclass(class) 【Student class】 ->
Superclass(class) 【【Student class】 superclass】 ->
Root class(class) ->
nil
元类的父类走向
打印以下代码
- (void)printSuperClass:(id)objc
{
NSLog(@"*********************************************");
NSLog(@" >>>>>> 待验证属性 >>>>>>");
Class class = object_getClass([Person class]);
NSLog(@"一Person的元类对象 - %p %@",class ,class);
NSLog(@"一NSObject的类对象 - %p %@",[NSObject class],[NSObject class]);
class = object_getClass([NSObject class]);
NSLog(@"一NSObject的(根)元类对象 - %p %@\n",class,class);
NSLog(@" >>>>>> objc 相关属性>>>>>>");
class = object_getClass([objc class]); /// 元类
class = [class superclass];
NSLog(@"一元类对象所属父类 - %p %@",class,class);
class = [class superclass];
NSLog(@"二父类 - %p %@",class,class);
class = [class superclass];
NSLog(@"三父类 - %p %@",class,class);
class = [class superclass];
NSLog(@"四父类 - %p %@",class,class);
class = [class superclass];
NSLog(@"五父类 - %p %@",class,class);
NSLog(@"*********************************************");
}
最后的打印结果
*********************************************
>>>>>> 待验证属性 >>>>>>
一Person的元类对象 - 0x10c81b8f8 Person
一NSObject的类对象 - 0x7fff89aec180 NSObject
一NSObject的(根)元类对象 - 0x7fff89aec158 NSObject
>>>>>> objc 相关属性>>>>>>
一元类对象所属父类 - 0x10c81b8f8 Person
二父类 - 0x7fff89aec158 NSObject
三父类 - 0x7fff89aec180 NSObject
四父类 - 0x0 (null)
五父类 - 0x0 (null)
*********************************************
由此打印可以验证文章开头的那张图的元类对象的父类走向
Subclass(meta) ->
Superclass(meta) ->
Root class(meta) ->
Root class(class) -> nil