IOS 底层原理之isa结构分析& 关联类

前言

通过 iOS 底层原理之 alloc & init & new 探究这篇文章,我们知道alloc一个对象,最核心的三个方法cls->instanceSize 计算内存大小 , (id)calloc(1, size)开辟内存返回地址指针, obj->initInstanceIsa初始化isa关联类,那么isa结构是到底是什么样的呢?到底是怎么关联的呢?下面我们进行探究

准备工作

联合体(union)

定义:当多个数据需要共享内存或者多个数据每次只取其一时 ,可以利用联合体(union)

  • 联合体可以定义多个不同类型的成员,但是联合体的内存大小由其中最大的成员的大小决定
  • 联合体变量共享同一块内存大小,成员之间是互斥的,一次只能使用其中的一个成员
  • 对联合体中某一个成员赋值,会覆盖其它成员的值
  • 它的所有成员都是从offset0的地方开始存储的

位域(Bit field)

有些信息在存储时,并不需要占用一个完整的字节, 而只需占 几个一个二进制位。例如在存放一个只有01两种状态成员, 用一位二进位即可,节省存储空间,并使处理简便

下面探讨isa结构时,会对联合体 &位域 进行实例探讨

isa 结构分析

我们按照alloc的流程,走到obj->initInstanceIsa,那么接着往下走

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}

点击进入 initIsa

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa = isa_t((uintptr_t)cls);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}

点击进入isa_t

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

代码中可以看出isa_t是一个联合体,占8字节,既64位,优化之前isa只是一个指向类或者元类指针,优化之后采用联合体+位域存储更多的信息,提高了内存的利用率

  • isa_t结构体中有两个成员 clsbitsisa_t是联合体,所以clsbits互斥的,既只能同时给一个变量赋值
  • isa_tISA_BITFIELD 是一个,用来区分不同的架构既 arm64(真机)和x86_64

bits的64位存储分布图(已真机为例)

各存储变量的含义:

  • nonpointer:表示是否对isa指针进行优化,0表示纯指针,1表示不止是类对象的地址,isa中包含了类信息、对象、引用计数等
  • has_assoc:关联对象标志位,0表示没有,1表示有
  • has_cxx_dtor:该对象是否C ++ 或者Objc的析构器,如果有析构函数,则需要做析构逻辑,没有,则释放对象
  • shiftcls:储存类指针的值,开启指针优化的情况下,在arm64架构中有33位用来存储类指针,x86_64架构中占44
  • magic:用于调试器判断当前对象是真的对象还是没有初始化的空间
  • weakly_referenced:指对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放
  • deallocating:标志对象是否正在释放
  • has_sidetable_rc:当对象引用计数大于10时,则需要借用该变量进位
  • hextra_rc:表示该对象的引用计数值,实际上引用计数值减1,例如,如果对象的引用计数为10,那么extra_rc9,如果大于10,就需要用到上面的has_sidetable_rc

isa_t总结,优化之后的isa,包含了优化之前类的指针shiftcls,仍然可以通过isa找到对应的类。同时还包含了更多其他的内容

isa 关联类

首先我们定义一个LWPerson类,初始化LWPerson alloc,流程如下:alloc ->_class_createInstanceFromZone -> initInstanceIsa ->initIsa,现在我们探究initIsa方法,isa是怎么和类关联起来的

从图中断点走位来看nonpointer =1,说明isa不是纯指针,打印看下newisa包含了哪些信息

newisa.bits = ISA_MAGIC_VALUEbits一个初始值,而ISA_MAGIC_VALUE是一个宏 # define ISA_MAGIC_VALUE 0x001d800000000001ULL0x001d800000000001二进制

可以看到第一位是1 nonpointer 值也是1,在x86_64magic 的位域分布47 - 52位,占6位,打印中显示 magic = 59,把它转成二进制

很明显可以看出,bitsmagic59是被ISA_MAGIC_VALUE赋值的
接着往下看

总结:从图中cls = LWPerson可以看出此时的isa已经关联了类,shiftclass保存了类信息
探究 newisa.shiftcls = (uintptr_t)cls >> 3

我们按照 cls >> 3 算法寄过打印的值和shiftcls的值是一样的,验证了shiftcls 存储着类信息

下面再添加一种验证算法


因为在x86_64中 ,shiftcls44位,从3 - 46位,将isa的内存地址右移3位,再左移20位,再右移17位,这时再打印地址isa的地址, 发现就是LWPerson,说明isa和类是关联的

isa指针移动图,暂且先不画了(可参照”bits的64位存储分布图“,进行移动),要是不明白的可以留言,或者后面把图补上

你可能感兴趣的:(IOS 底层原理之isa结构分析& 关联类)