美文网首页ios
iOS-底层(6):类的结构分析

iOS-底层(6):类的结构分析

作者: 恍然如梦_b700 | 来源:发表于2020-09-14 18:58 被阅读0次

    前几篇文章我们讲到,实例对象的isa指向了类,那么类的isa又指向了谁呢
    我们通过LLDB调试一下一探究竟


    16000710050301.jpg

    我们发现类的isa为FYPerson,那么这个FYPerson是什么呢?由此我们引出一个概念: 元类

    元类

    类本身也是一个对象,类对象的isa的位域的内容为元类

    • 元类的定义和创建都是由编译器完成的,类的归属即为元类
    • 元类本身是没有名称的,由于与类相关联,所以使用了同类名一样的名称
    16000718812807.jpg

    从上面的打印过程可以总结为:

    • 对象isa 指向 (也可称为类对象)
    • isa 指向 元类
    • 元类isa 指向 根元类,即NSObject
    • 根元类isa 指向 它自己

    元类内存中只存在存在一份根元类NSObject,根元类的元类是指向它自己.

    这个过程也是我们经常看到的著名的一张图

    isa流程图.png

    isa走位

    isa的走向有以下几点说明:

    • 实例对象(Instance of Subclass)isa 指向 类(class)

    • 类对象(class) isa 指向 元类(Meta class)

    • 元类(Meta class)isa 指向 根元类(Root metal class)

    • 根元类(Root metal class)isa 指向它自己本身,形成闭环,这里的根元类就是NSObject

    superclass走位

    superclass(即继承关系)的走向也有以下几点说明:

    • 之间 的继承关系:

      • 类(subClass) 继承自 父类(superClass)

      • 父类(superClass) 继承自 根类(RootClass),此时的根类是指NSObject

      • 根类 继承自 nil,所以根类NSObject可以理解为万物起源,即无中生有

    • 元类也存在继承,元类之间的继承关系如下:

      • 子类的元类(metal SubClass) 继承自 父类的元类(metal SuperClass)

      • 父类的元类(metal SuperClass) 继承自 根元类(Root metal Class

      • 根元类(Root metal Class) 继承于 根类(Root class),此时的根类是指NSObject

    • 【注意】实例对象之间没有继承关系之间有继承关系

    objc_class & objc_object

    我们接着看一下类编译成C++的代码

    16000731359950.jpg

    objc_object 是什么呢?
    我们看看底层objc4的源码:

    typedef struct objc_class *Class;
    
    /// Represents an instance of a class.
    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    
    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);
        }
        ...
    };
    

    总结

    可以看到,objc_class也是集成自objc_object,所有的对象都是基于objc_object结构体为模板创建出来的,所以所有的对象都有isa,这也侧面证明了万物皆对象的说法

    类结构分析

    我们补充一下内存偏移

    image.png

    从打印结果我们看出a和b的地址相差了4个字节,p1和p2相差了8个字节

    • p1 和p2 是指针,指向了[CJLPerson alloc]创建出来的内存地址
    • &p1和&p2是指针的地址,也就是二级指针
            int c[4] = {1, 2, 3, 4};
            int *d = c;
            NSLog(@"%p -- %p - %p", &c, &c[0], &c[1]);
            NSLog(@"%p -- %p - %p", d, d+1, d+2);
            for (int i = 0; i < 4; i++) {
                NSLog(@"%d ", *(d+i));
            }
    

    可以通过 首地址+偏移量取出数组中的其他元素,其中偏移量是数组的下标,内存中首地址实际移动的字节数 等于 偏移量 * 数据类型字节数
    操作符的作用:*取值 &取址

    2251862-cde021799373ba9f.png image.png image.png
    (lldb) p/x FYPerson.class
    (Class) $0 = 0x00000001000022d8 FYPerson
    (lldb) p (class_data_bits_t *)0x00000001000022f8
    (class_data_bits_t *) $1 = 0x00000001000022f8
    (lldb) p $1->data()
    (class_rw_t *) $2 = 0x0000000102148b40
    (lldb) p *$2
    (class_rw_t) $3 = {
      flags = 2148007936
      witness = 0
      ro_or_rw_ext = {
        std::__1::atomic<unsigned long> = 4294975608
      }
      firstSubclass = nil
      nextSiblingClass = NSUUID
    }
    (lldb) p $3.ro()
    (const class_ro_t *) $4 = 0x0000000100002078
    (lldb) p *$4
    (const class_ro_t) $5 = {
      flags = 388
      instanceStart = 8
      instanceSize = 32
      reserved = 0
      ivarLayout = 0x0000000100000f1b "\x03"
      name = 0x0000000100000f12 "FYPerson"
      baseMethodList = 0x00000001000020c0
      baseProtocols = 0x0000000000000000
      ivars = 0x0000000100002170
      weakIvarLayout = 0x0000000000000000
      baseProperties = 0x00000001000021d8
      _swiftMetadataInitializer_NEVER_USE = {}
    }
    (lldb) p $5.ivars
    (const ivar_list_t *const) $6 = 0x0000000100002170
    (lldb) p *$6
    (const ivar_list_t) $7 = {
      entsize_list_tt<ivar_t, ivar_list_t, 0> = {
        entsizeAndFlags = 32
        count = 3
        first = {
          offset = 0x0000000100002298
          name = 0x0000000100000f27 "enName"
          type = 0x0000000100000f79 "@\"NSString\""
          alignment_raw = 3
          size = 8
        }
      }
    }
    (lldb) p $7.get(0)
    (ivar_t) $8 = {
      offset = 0x0000000100002298
      name = 0x0000000100000f27 "enName"
      type = 0x0000000100000f79 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
    (lldb) p $7.get(1)
    (ivar_t) $9 = {
      offset = 0x00000001000022a0
      name = 0x0000000100000f2e "_name"
      type = 0x0000000100000f79 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
    (lldb) p $7.get(2)
    (ivar_t) $10 = {
      offset = 0x00000001000022a8
      name = 0x0000000100000f34 "_nickName"
      type = 0x0000000100000f79 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
    

    这些就是一些指针和值的取值取址,大家自己玩一玩就好

    总结:

    属性存在,方法,成员变量存在于bit,ivars里面不仅存的是实例属性,还会给属性生成_name这个实例变量

    相关文章

      网友评论

        本文标题:iOS-底层(6):类的结构分析

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