美文网首页
五 OC 底层原理 -- isa 的调用流程

五 OC 底层原理 -- isa 的调用流程

作者: 可乐冒气 | 来源:发表于2020-12-12 00:29 被阅读0次

    isa 结构回顾

    上篇 对象 与 isa 的关系 我们得知了 isa 连接了 对象 和 类(对象的 isa 指向了 对象所属的类对象)

    一. 对象

    当我们调用 obj.class 的时候为什么会返回 类相关的信息呢? 上篇我们知道 类相关的信息是存储在对象的 isa 中的,那我们是不是可以猜测 obj.class 对 isa 进行了相关的操作 取出了 isa

    - (Class)class {
        return object_getClass(self);
    }
    Class object_getClass(id obj)
    {
        if (obj) return obj->getIsa();
        else return Nil;
    }
    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;
    }
    
    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
    }
    
    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL
    # elif __x86_64__
    #   define ISA_MASK        0x00007ffffffffff8ULL
    # else
    #   error unknown architecture for packed isa
    # endif
    
    

    可以看到 isa 最终返回的是 (Class)(isa.bits & ISA_MASK); 在 isa.h 可以看到ISA_MASK的定义
    所以 当我们获取 class 的时候 本质是通过 isa的内存地址 进行内存运算,强转 Class 类型 获得的

    二. isa 流程图

    我们知道 对象的 isa 指向了类,那么类的 isa 指向的又是? 借用一张 苹果官方的 isa 流程图


    image.png

    我们现在来验证这幅图

    TObject *obj = [TObject new];
    

    断点断住 我们开始打印

    image.png
    通过位移运算 验证了 obj 的 isa -> TObject(类) image.png

    注意 两个 TObject 并不是同一个地址,所以不是同一个东西
    所以这也就是说 TObject(类) -> TObject(元类)

    image.png

    我们可以看到元类的isa 经过内存计算,打印出的是NSObjct也就是我们的根元类
    我们可以看到 根元类的地址 和 根元类 isa 指向的地址是同一片地址
    所以 也就有了 TObject(元类) -> NSObject(根元类) -> NSObject(根元类) 自己

    • 总结
    • obj对象 -> TObect类 -> TObject元类 -> NSObject根元类 -> NSObject根元类自己
      也就有了 官方图片中的虚线部分

    类的结构体

    在前面我们知道 isa 是一个 isa_t 的结构体

    typedef struct objc_class *Class;
    typedef struct objc_object *id;
    
    namespace {
        struct SideTable;
    };
    
    #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
    };
    

    之前讲到 isa_t 的结构体中 cls 存储的对象所属的类, 所以是Class 又是 objc_class 的结构体指针,也就是说类的本质 在底层 是 objc_class
    然而 objc_class 也继承于 objc_object, 也就是说类也是一个对象,所以第一个指针也是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
    
        class_rw_t *data() const {
            return bits.data();
        }
        void setData(class_rw_t *newData) {
            bits.setData(newData);
        }
    }
    截取了一部分源码
    

    superclass 父类

    所以 第二段地址就是 父类
    我们现在开始打印

    类的继承关系

    • 定义继承关系
      TObjectChildren -> TObject -> NSObject
    • 验证类的继承关系


      image.png

    通过验证得知 类的继承关系 TObjectChildren -> TObject -> NSObject -> nil

    元类的继承关系

    那么元类的继承关系又是什么样的呢

    我们用上面类的地址,获取相应的元类

    // TObjectChildren 的内存信息
    (lldb) x/4gx obj.class
    0x1000082c0: 0x0000000100008298 0x0000000100008270
    0x1000082d0: 0x0003000100620150 0x0004802100000000
    // TObjectChildren 的元类
    (lldb) p/x 0x0000000100008298 & 0x00007ffffffffff8ULL
    (unsigned long long) $14 = 0x0000000100008298
    (lldb) po 0x0000000100008298
    TObjectChildren
    // TObjectChildren 的元类的内存地址
    (lldb) x/4gx 0x0000000100008298
    0x100008298: 0x00000002101712a0 0x0000000100008248 -> TObjectChildren 的元类的父类地址
    0x1000082a8: 0x00010001007367f0 0x0002e03500000000
    
    
    
    //  TObject  内存信息
    (lldb) x/4gx 0x0000000100008270
    0x100008270: 0x0000000100008248 0x00000002101712c8
    0x100008280: 0x00000001a7e32f00 0x0000802100000000
    // TObject 的元类
    (lldb) p/x 0x0000000100008248 & 0x00007ffffffffff8ULL
    (unsigned long long) $18 = 0x0000000100008248 -> TObject 的元类地址
    (lldb) po 0x0000000100008248
    TObject
    
    ----
    我们从 0x0000000100008248 他是 TObjectChildren 的元类的父类的地址
    在下方的TObject的元类地址与其相同
    !!!!!!判定  TObjectChildren 的元类  继承于  TObject 的元类
    ----
    TObject 的元类内存信息
    (lldb) x/4gx 0x0000000100008248
    0x100008248: 0x00000002101712a0 0x00000002101712a0->TObject 的元类的父类地址
    0x100008258: 0x0001000100620300 0x0002e03500000000
    
    //NSObject 内存信息
    (lldb) x/4gx 0x00000002101712c8
    0x2101712c8: 0x00000002101712a0 0x0000000000000000
    0x2101712d8: 0x000100010054a8b0 0x0001801000000000
    // NSObject 元类
    (lldb) p/x 0x00000002101712a0 & 0x00007ffffffffff8ULL
    (unsigned long long) $20 = 0x00000002101712a0-> NSObject 的元类地址
    (lldb) po 0x00000002101712a0
    NSObject
    
    ----
    我们从 0x00000002101712a0 他是 TObject 的元类的父类的地址
    在下方的NSObject的元类地址与其相同
    !!!!!!判定  TObject 的元类  继承于  NSObject 的元类
    
    ----
    
    // NSObject 元类的内存信息
    (lldb) x/4gx 0x00000002101712a0
    0x2101712a0: 0x00000002101712a0 0x00000002101712c8
    0x2101712b0: 0x0003000100620200 0x0003e03400000000
    
    ---
    0x00000002101712c8 可以看出 NSObject的元类的父类地址 和 NSObject 的类地址相同
    而 NSObject 的父类地址为空
    所以 NSObejct的元类 继承于 NSObject 类 -继承于 nil
    ---
    

    通过一步一步的调试我们得知
    TObjectChildren元类 -> TObject元类 -> NSObject元类 -> NSObject类 -> nil

    相关文章

      网友评论

          本文标题:五 OC 底层原理 -- isa 的调用流程

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