iOS 底层-- isa指向探究

关系总揽


⬇️首先你需要知道有这张图的存在,具体关系在下面在一步一步来验证


isa流程图

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

获得实例对象objc的isa地址

我们得到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

你可能感兴趣的:(iOS 底层-- isa指向探究)