美文网首页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