美文网首页
iOS - isa的初始化&指向分析

iOS - isa的初始化&指向分析

作者: e521 | 来源:发表于2020-01-24 10:56 被阅读0次

    isa结构及初始化分析

    什么是isa,首先我们先看一下isa的结构:

    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的本质就是一个联合体:联合体中各元素共享内存,并互斥,且isa总共占有8字节.
    我们来看一下宏定义ISA_BITFIELD存的又是什么呢?以x86_64为例:

    #   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
    /**
      位域的结构:   类型说明符 位域名:位域长度
      ISA_BITFIELD所占字节数: 1+1+1+44+6+1+1+1+8 = 64bit,即8个字节
    */
    
    
    ISA_BITFIELD:
    1. nonpointer :1 表示是否对isa指针开启指针优化;0代表纯isa指针,1代表不止是类对象指针,还包含了类信息、对象的引用计数等;
    2. has_assoc : 1 关联对象标志位,0没有,1存在;
    3. has_cxx_dtor :1 该对象是否有C++或者Objc的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象;
    4. shiftcls :33 存储类指针的值。开启指针优化的情况下,在arm64架构中有33位用来存储类指针;
    5. magic :6 用于调试器判断当前对象是真的对象还是没有初始化的空间;
    6. weakly_referenced :1 标志对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放;
    7. deallocating :1 标志对象是否正在释放内存;
    8. has_sidetable_rc :1 当对象引用计数大于10时,则需要借用该变量存储进位
    9. extra_rc :19 当表示该对象的引用计数值,实际上是引用计数值减1,例如,如果对象的引用计数为10,那么extra_rc为9.如果引用计数大于10,则需要使用上面提到的has_sidetable_rc。

    值得注意的一点是isa的联合体中存了一个cls,其主要作用是什么呢?我们能在isa的初始化中找到答案:

    objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
    { 
        assert(!isTaggedPointer()); 
        
        if (!nonpointer) {
            isa.cls = cls;//如果是!nonpointer类型则直接将类与isa进行绑定
        } 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; //如果是nonpointer类型则通过位域中的shiftcls将类与isa进行绑定
    
    #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;
        }
    }
    

    由源码我们可以看出:如果是!nonpointer类型则直接将类与isa进行绑定,如果是nonpointer类型则需通过将cls进行位运算之后与shiftcls进行绑定.

    isa指向分析

    我们通过以下代码调用object_getClass()来探讨一下isa中类的绑定:

    Class object_getClass(id obj)
    {
        if (obj) return obj->getIsa();
        else return Nil;
    }
    
    getIsa:
    bjc_object::getIsa() 
    {
        if (!isTaggedPointer()) return ISA();//// 一般都不是TaggedPointer
    
        uintptr_t ptr = (uintptr_t)this;
        if (isExtTaggedPointer()) {
            uintptr_t slot = 
                (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
            return objc_tag_ext_classes[slot];
        } else {
            uintptr_t slot = 
                (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
            return objc_tag_classes[slot];
        }
    }
    
    ISA():
    objc_object::ISA() 
    {
        assert(!isTaggedPointer()); 
    #if SUPPORT_INDEXED_ISA
        /**此处是iWatch OS,不执行*/;
    #else
        return (Class)(isa.bits & ISA_MASK);
    #endif
    }
    
    # elif __x86_64__
    #   define ISA_MASK        0x00007ffffffffff8ULL
    

    通过以上源码可以发现获取对象的类就是获取对象的isa,而isa通过位域&上一个mask(isa.bits & ISA_MASK),就可以获取类。

    下面我们就通过LLDB对Student *student = [Student alloc]的调试来进行验证:

    由此可以看出通过isa.bits & ISA_MASK方式,确实可以获取到student的类;我们继续打印继续往下研究发现如下结果:


    由此我们可以看出isa的走位为:


    isa走位图
    小结:

    1.实例对象的isa指向的是类;
    2.类的isa指向的元类;
    3.元类指向根元类;
    4.根元类指向自己;

    最后附上官方的isa流程图:
    isa流程图.png

    相关文章

      网友评论

          本文标题:iOS - isa的初始化&指向分析

          本文链接:https://www.haomeiwen.com/subject/fcscthtx.html