isa结构分析

在之前的文章OC对象的alloc过程中,我们探讨了OC对象初始化的主要过程,在第三步,主要是调用 initInstanceIsa方法,来初始化 isa指针关联类信息的,在这里,我们来共同探讨该过程。

分析

我们定义一个LYPerson类,并进行初始化

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LYPerson *person = [LYPerson alloc];
    }
    return 0;
}

按照OC对象的alloc过程 中的过程方法,我们定位到 initInstanceIsa方法

    // 3:
    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);
    }

接下来我们就能定位到initIsa

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);
        .......
    }
}

接下来我们可以看到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
};

x86架构下,其 位域如下

      uintptr_t nonpointer        : 1;    // 1                                     
      uintptr_t has_assoc         : 1;    // 2                                     
      uintptr_t has_cxx_dtor      : 1;  // 3                                      
      uintptr_t shiftcls          : 44;  // 4 /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \          
      uintptr_t magic             : 6;    // 5                                     
      uintptr_t weakly_referenced : 1;   // 6                                         
      uintptr_t deallocating      : 1;    // 7                                     
      uintptr_t has_sidetable_rc  : 1;    // 8                                     
      uintptr_t extra_rc          : 8    // 9
  • nonpointer,has_assoc,has_cxx_dtor各占1位
  • shiftcls44位
  • magic6位
  • weakly_referenceddeallocatinghas_sidetable_rc各占1位
  • extra_rc8位
各个位表示什么含义呢????
关联类

首先我们先来探索 类是如何关联到isa的,在 initIsa方法中

        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; // 1
  • 1,将 cls进行了强制类型转换右移3位存到 shiftcls

我们分析如下

(lldb) po cls // 1
LYPerson

(lldb) p/x cls
(Class) $10 = 0x0000000100002170 LYPerson

(lldb) p/x (uintptr_t)cls >> 3 // 2
(uintptr_t) $0 = 0x000000002000042e

(lldb) p/x newisa // 3
(isa_t) $3 = {
  cls = 0x001d800100002171 LYPerson
  bits = 0x001d800100002171
   = {
    nonpointer = 0x0000000000000001
    has_assoc = 0x0000000000000000
    has_cxx_dtor = 0x0000000000000000
    shiftcls = 0x000000002000042e
    magic = 0x000000000000003b
    weakly_referenced = 0x0000000000000000
    deallocating = 0x0000000000000000
    has_sidetable_rc = 0x0000000000000000
    extra_rc = 0x0000000000000000
  }
}

(lldb) po 0x001d800100002171 & 0x00007ffffffffff8ULL // 4
LYPerson

(lldb) p/x 0x001d800100002171 >> 3    // 5
(long) $7 = 0x0003b0002000042e
(lldb) p/x 0x0003b0002000042e << 20
(long) $8 = 0x0002000042e00000
(lldb) p/x 0x0002000042e00000 >> 17
(long) $9 = 0x0000000100002170

(lldb) po 0x0000000100002170
LYPerson
  • 1,在初始化isa时,传入的clsLYPerson
  • 2,将 cls进行类型转换,转换成 uintptr_t右移3位,将结果存入到shiftcls位置中,即联合体中3 ~ 46中,这样就存储了类信息,进行了类关联
  • 3,将 isa指针进行打印
  • 4,使用 ISA_MASKisa指针来获取类名 。我们将ISA_MASK(0x00007ffffffffff8ULL)转化为二进制格式

isa1.png

我们可以看出它就是 总长度为 64,中间44位值为 1,其余位数的值为0的二进制数,进行 与运算时,是取得中间44位的值

  • 5,从另一方面,取出中间 44的值,将 isa指针值右移3位,将前3位进行抹零,然后左移20位,将后17位进行抹零,最后,恢复中间的44位,得到值就为关联类

小计:中间的shiftcls存储的信息为类的信息

has_assoc

根据源码

inline void
objc_object::setHasAssociatedObjects()
{
    if (isTaggedPointer()) return;

 retry:
    isa_t oldisa = LoadExclusive(&isa.bits);
    isa_t newisa = oldisa;
    if (!newisa.nonpointer  ||  newisa.has_assoc) {
        ClearExclusive(&isa.bits);
        return;
    }
    newisa.has_assoc = true; // 1
    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
  • 1,关联属性在赋值时,会讲has_assoc标志位改为true

由于篇幅所限,对于其他标志位不在一一分析了,读者可自行探索。

总结

我们分析了isa的结构组成,它由联合体组成,里面有64位,它们分别代表了不同的含义。然后,我们又分析了isa关联类的实现和原理。下面我们在总结下每个标志位的作用。

1,nonpointer:表示是否对 isa 指针开启指针优化,0:纯isa指针,1:不止是类对象地址,isa中包含了类信息对象的引用计数等。
2,has_assoc:关联对象标志位,0没有,1存在
3,has_cxx_dtor: 该对象是否有 C++ 或者 Objc的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象。
4,shiftcls:存储类指针的值。
5,magic: 用于调试器判断当前对象是真的对象还是没有初始化的空间。
6,weak_referenced:该对象是否被指向或者曾经指向一个 ARC的弱变量,没有弱引用的对象可以更快释放。
7,deallocating:标志对象是否在释放内存。
8,hsa_sidetable_rc:当对象引用计数大于10时,则需要借助该变量存储进位。
9,extra_rc:当表示该对象的引用计数值,实际上是引用计数值减1.

你可能感兴趣的:(isa结构分析)