美文网首页
003-OC对象原理探究 - isa 和 nonpointer

003-OC对象原理探究 - isa 和 nonpointer

作者: Mr_wick | 来源:发表于2021-06-17 19:41 被阅读0次

    引言

    001-OC对象原理探究 - alloc这篇文章的探索过程中,大体上讲述了alloc的基本流程。其中探索到_class_createInstanceFromZone方法中的下面这段代码时,简单讲了对象的关联。

    if (!zone && fast) {
            obj->initInstanceIsa(cls, hasCxxDtor);
        } else {
            obj->initIsa(cls);
        }
    

    因此,本文将详细探究这个神秘的isa

    initInstanceIsainitIsa源码

    我们在源码中,点击initInstanceIsa(cls,hasCxxDtor)进入到objc-object.h中可以看到,函数内部做了两个断言

    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
    

    1、instancesRequireRawIsa()内部为cache.getBit(FAST_CACHE_REQUIRES_RAW_ISA);。其中FAST_CACHE_REQUIRES_RAW_ISA为类的cache_t中的uint16_t _flags第13位是否为1,以此来表示类对象(非实例对象)是否需要原始的isa。

    2、cls->hasCxxDtor()内部为cache.getBit(FAST_CACHE_HAS_CXX_DTOR);。其中FAST_CACHE_HAS_CXX_DTOR为类cache_tuint16_t _flags第2位是否为1,以此来表示当前类或者父类是否有c++的析构函数的实现。

    接下来就是进入到initIsa(cls, true, hasCxxDtor);内部源码如下:

    inline void 
    objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
    { 
        ASSERT(!isTaggedPointer()); 
        
        isa_t newisa(0);
    
        if (!nonpointer) {
            newisa.setClass(cls, this);
        } else {
            ASSERT(!DisableNonpointerIsa);
            ASSERT(!cls->instancesRequireRawIsa());
    #if SUPPORT_INDEXED_ISA
            ASSERT(cls->classArrayIndex() > 0);
            newisa.bits = ISA_INDEX_MAGIC_VALUE;
            newisa.has_cxx_dtor = hasCxxDtor;
            newisa.indexcls = (uintptr_t)cls->classArrayIndex();
    #else
            newisa.bits = ISA_MAGIC_VALUE;
    #   if ISA_HAS_CXX_DTOR_BIT
            newisa.has_cxx_dtor = hasCxxDtor;
    #   endif
            newisa.setClass(cls, this);
    #endif
            newisa.extra_rc = 1;
        }
        isa = newisa;
    }
    

    1、由initInstanceIsa调用的initIsanonpointer传的值为true,走的是else内部逻辑,对newisa多项赋值。
    2、由obj->initIsa(cls);调用的initIsanonpointer传的值为false,走的是if内部逻辑,只对newisa的class设置即可。
    3、在initIsa方法中,首先第一句就断言ASSERT(!isTaggedPointer());是否为TaggedPointer,如果是TaggedPointer则无法继续后续的逻辑,即无isa

    isa的内部结构以及nonpointerTaggedPointer的解释

    isa结构源码如下,是一个联合体+位域的结构:
    联合体union内部成员为互斥存在,即联合体所占内存大小决定于内部最大成员所占大小。结构体struct则是“有容乃大”,占内存大小可参考002-OC对象原理探究 - 结构体内存对齐

    union isa_t {
        isa_t() { } // 构造方法
        isa_t(uintptr_t value) : bits(value) { } // 构造方法
        uintptr_t bits;
    private:
        Class cls;
    
    public:
    #if defined(ISA_BITFIELD)
        struct {
            ISA_BITFIELD;  // 此宏为isa内部位域的分配情况,源码在下面
        };
        bool isDeallocating() {
            return extra_rc == 0 && has_sidetable_rc == 0;
        }
        void setDeallocating() {
            extra_rc = 0;
            has_sidetable_rc = 0;
        }
    #endif
        void setClass(Class cls, objc_object *obj);
        Class getClass(bool authenticated);
        Class getDecodedClass(bool authenticated);
    };
    

    其中ISA_BITFIELD此宏为isa内部位域的分配情况:(:1:44等分别表示该成员占1位44位

    # if __arm64__                                                                                                    
            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 unused            : 1;                                       
            uintptr_t has_sidetable_rc  : 1;                                       
            uintptr_t extra_rc          : 19
    # elif __x86_64__                      
            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 unused            : 1;                                         
            uintptr_t has_sidetable_rc  : 1;                                         
            uintptr_t extra_rc          : 8
    

    -nonpointer: 是否开启NONPOINTER isa指针优化;
    -has_assoc: 对象是否含有关联引用
    -has_cxx_dtor:对象是否含有 C++ 或者 Objc 的析构器
    -shiftcls: 类的指针(重点)(arm64:33位,x86_64:44位)
    -magic: 对象是否初始化完成 (arm64:0x16 ,x86_64:0x3b)
    -weakly_referenced:是否为弱引用的对象
    -deallocating:对象是否正在执行析构函数(是否在释放内存)
    -has_sidetable_rc:判断是否需要用sidetable去处理引用计数,(extra_rc的大小影响到这个变量)
    -extra_rc: 存储该对象的引用计数值减一后的结果
    isa的二进制分布如图所示:

    isa64情况.jpeg

    TaggedPointer

    早期64位架构中,存储基础数据类型 , 底层会封装成 NSNumber , 在开辟8字节内存,32位架构开辟4字节。维护引用计数,管理生命期 。造成运行效率上的损失 。会造成很大空间浪费。因此,引入TaggedPointer。当断言为TaggedPointer,则对象指针的值不是地址了,而是真正的值,直接优化了内存,提升了获取速度。TaggedPointer的更多知识,可阅读这篇文章

    nonpointer

    ISA_BITFIELD内部,可看到nonpointer占1位,表示是否对 isa 指针开启指针优化(0:纯isa指针,1:不⽌是类对象地址,isa 中包含了类信息、对象的引⽤计数等)。应用判断,在上面已说明。

    isa的用法

    isa的用法和isa的走向将在下篇文章探究。

    总结

    每个类都有一个isa,isa的探究重要性不言而喻。我们通过alloc流程探究到isa的内部结构,补上了001文章的alloc流程的一个坑。好累,拜拜。。

    相关文章

      网友评论

          本文标题:003-OC对象原理探究 - isa 和 nonpointer

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