isa与类的关联

在alloc&init&new探索中我们知道,alloc一个对象主要有cls->instanceSize计算需要的内存大小、calloc(1, size)根据所需要的内存大小开辟内存空间、initInstanceIsa(cls, hasCxxDtor)isa与内关联;今天就来探索一下isa是如何与类产生关联的。
准备工作:objc4-781源码

obj->initInstanceIsa(cls, hasCxxDtor);

    if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }
  • 跟进initInstanceIsa(cls, hasCxxDtor)
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}
  • 这我们看出,接下来会执行initIsa(cls, true, hasCxxDtor)
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是在这里完成配置的。 isa的类型是isa_t。那么isa到到底是怎么配置的呢?

isa_t

我们跟进isa_t发现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类型使用联合体是基于内存优化,采用char+位域(即二进制中每一位均可以保存不同的信息)的原理。我们都知道isa指针占用8个字节,即64位,可以合理的将64位利用起来,极大的节约了内存空间。

  • 联合体isa_t提供了两个成员变量clsbits,由联合体定义可知clsbits互斥。
  • 还提供了一个位域机构体,在这个位域机构体中是一个宏ISA_BITFIELD,根据释义我们知道这个宏定义在isa.h
    • 位域结构体
      按二进制位来分配内存的结构体,冒号表示位域。位域出现的原因是由于某些信息的存储表示只需要几个bit位就可以表示而不需要一个完整的字节,同时也是为了节省存储空间和方便处理

ISA_BITFIELD

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

我们可以看出整个定义的联合体位域无论是在__arm64__ (iOS)还是__x86_64__(macOS)中都占64位。32位这里就探索了,一样的原理。

  • nonpointer:是否对isa指针开启指针优化。有两个值,表示自定义的类等,占1位
    • 0:纯isa指针
    • 1:不只是类对象地址,isa中包含了类信息、对象的引用计算等
  • has_assoc:是否有关联对象
  • has_cxx_dtor:是否有C++的相关实现。表示该对象是否有C++/OC析构器(类似于dealloc),占1位
    • 如果有析构函数,则需要做析构相关的逻辑
    • 如果没有,则可以快速释放
  • shiftcls:存储类信息
    • __arm64__中占33位
    • __x86_64__中占44位
  • magic:用于调试器判断当前对象真的对象还是没有初始化的空间 ,占6位
    • __arm64__: 0x1a
    • __x86_64__:0x3b
  • weakly_referenced:指对象是否被指向或者曾经指向一个ARC的弱变量
    • 如果没有可以更快释放
  • deallocating:标志对象是否正在释放内存
  • has_sidetable_rc:是否有外挂的散列表。当对象的引用计数大于10时,则需要借用该变量存储进位
  • extra_rc:额外的引用计数,实际上是引用计数值减1,也就是说如果对象引用计数为10,那么extra_rc是9.如果引用计数大于10,则需要用到has_sidetable_rc

isa

isa流程图.png

从这张图中我们可以看到类的实例的isa指向了isa指向的它的元类,而元类的isa指向了根元类根元类的isa还是指向本身,但是根元类NSObject也继承自类NSObject

你可能感兴趣的:(isa与类的关联)