OC isa分析

作者: H丶ym | 来源:发表于2020-09-15 13:37 被阅读0次

    OC底层原理学习

    这一章我们来探索一下alloc 源码核心中的
    obj->initInstanceIsa(cls, hasCxxDtor) :将类与isa指针关联
    step1:来到objc-object.h 中的initInstanceIsa的源码中

    inline void 
    objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
    {
        ASSERT(!cls->instancesRequireRawIsa());
        ASSERT(hasCxxDtor == cls->hasCxxDtor());
    
        initIsa(cls, true, hasCxxDtor);
    }
    

    step2:来到objc-object.h中的initIsa的源码中

    inline void 
    objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
    { 
        ASSERT(!isTaggedPointer()); 
        
        if (!nonpointer) {
            isa = isa_t((uintptr_t)cls); //isa 初始化
        } else {
            ASSERT(!DisableNonpointerIsa);
            ASSERT(!cls->instancesRequireRawIsa());
    
            isa_t newisa(0);//isa 初始化
    
    #if SUPPORT_INDEXED_ISA // 通过cls初始化
            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 // 通过bits初始化
            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;
            //shiftcls 从第三位开始
            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;
        }
    }
    

    我们可以看到isaisa_t类型的
    来到objc-private.hisa_t的源码

    //联合体
    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
        //提供了cls 和 bits ,两者是互斥关系
        Class cls;
        uintptr_t bits;
    #if defined(ISA_BITFIELD)
        struct {
            ISA_BITFIELD;  // defined in isa.h
        };
    #endif
    };
    

    联合体
    联合体也是由不同的数据类型组成,但其变量是互斥的,所有的成员共占一段内存。而且共用体采用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会将原来成员的值覆盖掉

    • 优点:所有成员共用一段内存,使内存的使用更为精细灵活,同时也节省了内存空间
    • 缺点:包容性弱

    与结构体的区别
    结构体的各个成员会占用不同的内存,互相之间没有影响
    共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员
    结构体内存 >= 所有成员占用的内存总和(成员之间可能会有缝隙)
    共用体占用的内存等于最大的成员占用的内存

    isa_t是一个union联合体

    • Class cls代表isa关联的类的类型,
    • uintptr_t bits是一段保存着isa指针内存优化,这里的内存优化是指在isa指针中通过char + 位域(即二进制中每一位均可表示不同的信息)的原理实现。通常来说,isa指针占用的内存大小是8字节,即64位,已经足够存储很多的信息了,这样可以极大的节省内存,以提高性能
    • ISA_BITFIELD,bits的位域,是一个宏定,全局搜索ISA_BITFIELDisa.h中可以找到它的定义
      image.png

    __arm64__iOS(真机)
    __ax86_64__macOS(模拟器)
    uintptr_t nonpointer:是否对isa指针开启指针优化
    uintptr_t has_assoc:关联对象标志位
    uintptr_t has_cxx_dtor:表示该对象是否有C++/OC的析构器
    uintptr_t shiftcls:存储类的指针的值(类的地址),真机占33位/模拟器占44位
    uintptr_t magic:调试器判断对象是真对象还是未初始化空间
    uintptr_t weakly_referenced:对象是否被指向或者曾经指向一个ARC的若变量
    uintptr_t deallocating:标志对象是否正在释放内存
    uintptr_t has_sidetable_rc:当对象引用计数大于10时,则需要借用该变量存储进位。
    uintptr_t extra_rc:表示该对象的引用计数值,实际上是引用计数值减1,例如,如果对象的引用计数为10,那么extra_rc为9,如果大于10,就需要用到上面的has_sidetable_rc真机占19位/模拟器占8位

    bits位域分布

    initIsa源码分析
    运行objc4-781源码,来到initIsa方法,添加断点,

    step1 在初始化后打印newisa

    isa初始值

    step2bits赋值ISA_MAGIC_VALUE
    ISA_MAGIC_VALUE宏源码
    define ISA_MAGIC_VALUE 0x001d800000000001ULL
    打印newisa

    bits赋值

    bits赋值后,magic = 59,59的二进制 0x001d800000000001ULL

    step3 shiftcls赋值(uintptr_t)cls >> 3

    shiftcls 赋值
    shiftcls赋值后,cls也有值了

    isa指针的应用

    平时开发过程中,我们是通过object_getClass获取类名称
    看下这个方法的源码
    来到objc-class.mm

    Class object_getClass(id obj)
    {
        if (obj) return obj->getIsa();
        else return Nil;
    }
    

    来到objc-object.h中的getIsa

    inline Class 
    objc_object::getIsa() 
    {
        if (fastpath(!isTaggedPointer())) return ISA();
    
        extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
        uintptr_t slot, ptr = (uintptr_t)this;
        Class cls;
    
        slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
        cls = objc_tag_classes[slot];
        if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
            slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
            cls = objc_tag_ext_classes[slot];
        }
        return cls;
    }
    

    来到objc-object.h中的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
    }
    

    原来重写了isaget方法,通过isa.bits & ISA_MASK获取bits里面存储的类信息,然后强转成Class类型

    相关文章

      网友评论

        本文标题:OC isa分析

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