isa

作者: 码农农农SL | 来源:发表于2019-11-24 13:23 被阅读0次

    如果对NSObject的实现有过研究,应该就知道,所有的对象,不管是实例对象,还是类对象,其实质都是一个C语言结构体

    typedef struct objc_class *Class;
    typedef struct objc_object *id;
    
    struct objc_object {
    private:
        isa_t isa;
    ...
    };
    
    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    ...
    };
    

    从以上结构体的定义,可以很清楚的看出,不管是实例对象还是类对象,其结构内的首个地址存储的就是isa指针,而isa指针的类型又为Class,即 struct objc_class,所以如果想对isa有一个更深入的认识,就需要对objc_class这个结构体进一步分析。

    注:

    struct objc_object {
    private:
        isa_t isa;
    ...
    };
    

    这个结构体里面的第一个虽然不是struct objc_class指针,但是细看isa_t的定义,实际上它还是返回的一个struct objc_class的指针:

    #include "isa.h"
    
    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
    };
    
    # 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)
    

    这部分是apple对isa的优化。自从引入tagged pointer之后,isa就再也不是单纯的指针的,所以有时候直接查看对象内的isa时,会发现他根本就不是一个指针:

    image.png
    可以看出,取出对象c中第一个地址的值,是0x000001a1000f133d,显然它不符合一个指针,因为在arm64下,指针是8字节对齐的,也就是说,指针最后一位,要么是0x0,要么是0x8,还有就是,/*MACH_VM_MAX_ADDRESS 0x1000000000*/,但是显然0x000001a1000f133d0x1000000000大的多。
    那么问题就来了:对于被优化的isa,我们该怎么去获取真正的isa指针呢?
    要回答这个问题,我们要先了解下object_getClass(id _Nullable obj)的实现:
    Class object_getClass(id obj)
    {
        if (obj) return obj->getIsa();
        else return Nil;
    }
    
    inline Class 
    objc_object::getIsa() 
    {
        if (!isTaggedPointer()) return ISA();
    ...
    }
    //可以看出,最终的调用,指向`ISA()`函数,那么我们就去看看`ISA()`里面的操作。
    

    runtime源码中找到ISA()

    inline Class 
    objc_object::ISA() 
    {
        assert(!isTaggedPointer()); 
    #if SUPPORT_INDEXED_ISA
        if (isa.nonpointer) {
            uintptr_t slot = isa.indexcls;
            return classForIndex((unsigned)slot);
        }
        return (Class)isa.bits;
    #else
        return (Class)(isa.bits & ISA_MASK);
    #endif
    }
    

    对于arm64,可以简化为:

    inline Class 
    objc_object::ISA() 
    {
        return (Class)(isa.bits & ISA_MASK);
    }
    

    这个isa.bits的值,在上例中为:0x000001a1000f133d,而ISA_MASK的定义为:

    #   define ISA_MASK        0x0000000ffffffff8ULL
    

    仔细观察这个0x0000000ffffffff8ULL,你会发现,它恰好在第3bit到第35bit的值为1,而&上isa.bits的意思是取isa.bits的3bit~35bit的值,结合刚刚给出的isa_t的定义:

    #   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
    

    正好对上了shiftcls,似乎一切都是巧合,但其实不是,早在对象初始化的时候,就已经将isa指针的值,存入shiftcls了:

            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;  //这里将cls的值往左移3bit的意思,为了节省内存,清空低三位不用的数,因为对于指针,低三位的值肯定是0。
    
    
    其他bit的含义:
    • has_assoc
      对象含有或者曾经含有关联引用,没有关联引用的可以更快地释放内存
    • has_cxx_dtor
      当前对象有 C++ 或者 ObjC 的析构器(destructor),如果没有析构器就会快速释放内存。
    • magic
      用于调试器判断当前对象是真的对象还是没有初始化的空间
    • weakly_referenced
      对象被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放
    • deallocating
      对象正在释放内存
    • has_sidetable_rc
      对象的引用计数太大了,存不下
    • extra_rc
      对象的引用计数超过 1,会存在这个这个里面,如果引用计数为 10,extra_rc 的值就为 9

    相关文章

      网友评论

          本文标题:isa

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