iOS底层学习 - isa指针的底层实现

本文的主要目的是理解isa指针相关的一些知识。

我们都知道oc对象的本质是一个结构体,想要更好的了解这个结构体,我们就需要用到apple主导编写的编译器clang,它是一个基于LLVM的C/C++/OC的编译器,主要是用于底层编译,将文件输出成c++文件,例如main.m 输出成main.cpp,其目的是为了更好的观察底层的一些结构 及 实现的逻辑,方便理解底层原理。
具体使用可参考:https://www.jianshu.com/p/9fc7776cce9b

在main中自定义一个类LGPerson,有一个属性name,通过终端,利用clang将main.m编译成 main.cpp


clang编译main.m.jpg

打开编译好的main.cpp,找到LGPerson的定义,发现LGPerson在底层会被编译成 struct 结构体。
LGPerson_IMPL中的第一个属性 其实就是 isa,是继承自NSObject,属于伪继承,伪继承的方式是直接将NSObject结构体定义为LGPerson中的第一个属性,意味着LGPerson 拥有 NSObject中的所有成员变量。
LGPerson中的第一个属性 NSObject_IVARS 等效于 NSObject中的 isa。

之前的博客中有画过一个alloc的底层执行流程分析图,我们来看看核心之一的initInstanceIsa方法,通过查看这个方法的源码实现,我们发现,isa指针的初始化,是通过isa_t这个类型来完成的,isa_t是使用联合体定义union,而在NSObject定义中isa的类型是Class,其根本原因是由于isa 对外反馈的是类信息,为了让开发人员更加清晰明确,需要在isa返回时做了一个类型的强制转换。

//initInstanceIsa
inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
    //初始化isa
    initIsa(cls, true, hasCxxDtor); 
}

//isa_t的定义
union isa_t { //联合体
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }
    //提供了cls 和 bits ,两者是互斥关系
    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

//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
}

联合体相比结构体,它也是由不同的数据类型组成,但其变量是互斥的,所有的成员共占一段内存。而且共用体采用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会将原来成员的值覆盖。
isa_t类型使用联合体的原因也是基于内存优化的考虑,这里的内存优化是指在isa指针中通过char + 位域(即二进制中每一位均可表示不同的信息)的原理实现。通常来说,isa指针占用的内存大小是8字节,即64位,已经足够存储很多的信息了,这样可以极大的节省内存,以提高性能。
从isa_t的定义中可以看出:

  • isa_t提供了两个成员,cls 和 bits,由联合体的定义所知,这两个成员是互斥的,也就意味着,当初始化isa指针时,有两种初始化方式:
    一: 通过cls初始化,bits无默认值
    二: 通过bits初始化,cls有默认值
  • 还提供了一个结构体定义的位域,用于存储类信息及其他信息,结构体的成员ISA_BITFIELD,这是一个宏定义,有两个版本 arm64(对应ios 移动端) 和 x86_64(对应macOS),以下是它们的一些宏定义:
    位域作用域.png

    通过initInstanceIsa进入其中的initIsa方法,其功能主要是初始化isa指针
    isa初始化.png

    从源码可以看出,两种初始化方式,cls和bits
    而且也可看出cls 与 isa 关联原理,就是在isa指针中的shiftcls位域中存储了类的信息,将cls进行编码后,将其转换为uintptr_t数据类型,使其可以被机器码识别,然后由于shiftcls处于isa指针地址的中间部分,前面还有3个位域,为了不影响前面的3个位域的数据,需要右移将其抹零。

参考博客:https://www.jianshu.com/p/7fd6241a7124

你可能感兴趣的:(iOS底层学习 - isa指针的底层实现)