ios 对象原理探索五-类的结构

366 阅读6分钟

ios对象原理分析四

前言

上篇文章分析了isa,并分析了它的结构,分析和验证了它是如何和类关联起来的,这片文章主要分析的是类的结构以及对象-类-元类-根元类之间的走位.

一. isa的指向以及类之间的关系

准备,创建2个类MRPerson和MRTeacher,MRTeacher类继承于MRPerson.在MRPerson定义两个变量和两个方法.(成员变量/属性)(实例方法和类方法)

@interface MRPerson : NSObject{
   ///成员变量
   NSStrin *hobby;
}
///属性
@property (nonatomic,copy) NSString *name;
///实例方法
-(void) sayHello;
///类方法
+(void)sayBye;
@end

在mian函数中,创建这两个类,并对齐内存结构进行LLDB调试

获取类的内存信息

打印类的isa指向的地址

如上的操作步骤:**person**isa(0x001d80010000228d)指向的是**MRPeson**类,**MRPeson**类的isa(0x0000000100002260)指向的还是**MRPeson**类,第二个**MRPeson**类的isa(0x00000001003330f0)指向的是**NSobject**

根据调试过程,我们产生了一个疑问:为什么图中的调试过程中出现了2个MRPerson和多次打印NSObject的isa指针指向的还是NSObject?

  1. 两个打印都是**MRPerson**的根本原因就是因为元类导致的

  2. 多次打印**NSObject** ,是因为根元类的isa指向自身导致的

元类说明:

  1. 万事万物皆对象,**实例对象**的isa指针指向**类**,**类**它也是一个对象,可以称之为**类对象**,那么问题来了,类对象的isa指针指向那? 苹果爸爸就提出了一个**元类**的概念,用来接受**类对象**的isa指针

  2. **元类是系统提供的**,它的创建是由**编译器自动完成的**,所以我们感受不到.

  3. **元类****类对象****类**,元类存储的是**类对象****方法**

**元类**本身是**没有名称的**,由于与**类****关联**,所以使用了**同类名一样的名称**

isa总结

从图中可以看出

  1. **对象****isa** 指向 **类**(也可称为**类对象**
  2. **类对象****isa** 指向 **元类**
  3. **元类****isa** 指向 **根元类**,即**NSObject**
  4. **根元类****isa** 指向 它**自己**
  • 由此就引出了**“cooci老师”**的著名的isa走位图

说明 1.实线代表继承关系,虚线代表isa指向关系

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

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

  2. 类对象(class) isa 指向 元类(Meta class)

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

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

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

  1. 之间 的继承关系:

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

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

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

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

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

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

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

【注意】:
    实例对象之间没有继承关系,只有类之间有继承关系

举例:

isa 走位

  • teacher的isa走位链:teacher(子类对象) --> MRTeacher (子类)--> MRTeacher(子元类) --> NSObject(根元类) --> NSObject(跟根元类,即自己)
  • person的isa走位图:person(对象) --> MRPerson (类)--> MRPerson(元类) --> NSObject(根元类) --> NSObject(根元类,即自己

二. 类的结构分析

在文章《ios对象原理探索三》源码初步查看了类的相关结构,以及知道了类实际上是一个结构体.在跟进过程中我们看到了两个结构体**objc_class****objc_object**下面就具体探索下这两个结构体

objc_object & objc_class

在源码中搜索:

上图源码可以看出objc_class继承于objc_object,进入objc_objec_t_源码**:**

总结:
**objc_class**继承于**objc_object**,**objc_class**中有一个**公用的isa**,所以所有的对象都**继承于objc_object**,万物皆来源于**objc_object**

**objc_class、objc_object、isa、object、NSObject**等的整体的关系,如下图所示

探索类信息中都有哪些内容

类的结构中有4部分内容

  1. isa 指针(共有属性)
  2. superclass 指向父类的指针
  3. cache缓存
  4. bits属性结构体(class_rw_t * plus custom rr/alloc flags)创建

类的属性存储的位置就是我们着重分析bits结构体

知识点:
内存偏移量:众所周知,内存的存储是顺序存储的,我们知道了内存的首地址,知道了相对于内存首地址数据首地址的大小,我们可以拿到任何存储在内存的数据,这个相对于内存首地址的大小就是内存的偏移量.

例如:

解释:

那么根据这个原理,就可以得出有关**bits**的内容

struct objc_class : objc_object {
    // Class ISA; //8字节
    Class superclass; //Class 类型 8字节
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    //....方法部分省略,未贴出
}

获取cache的大小

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets; // 是一个结构体指针类型,占8字节
    explicit_atomic<mask_t> _mask; //是mask_t 类型,而 mask_t 是 unsigned int 的别名,占4字节
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets; //是指针,占8字节
    mask_t _mask_unused; //是mask_t 类型,而 mask_t 是 uint32_t 类型定义的别名,占4字节

#if __LP64__
    uint16_t _flags;  //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节
#endif
    uint16_t _occupied; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节

cache的大小为16字节,所以有上述计算可知,想要获取bits的中的内容,只需通过首地址平移32字节即可

以下是通过lldb命令调试的过程

通过class_rw_t提供的方法,继续探索 bits中的属性列表

以下是lldb 探索的过程图示

上图可看出$6存储的是一个property_array_t的数组,继续获取里面的值

咋报错了??why?明明我定义的时候定义了一个属性和一个成员变量,怎么只用一个属性,成员变量呢?

由此可得出property_list 中只有属性,没有成员变量,属性与成员变量的区别就是有没有set、get方法,如果有,则是属性,如果没有,则是成员变量。

类的方法列表

方法和类的属性基本相同,我只书写下LLDB调试的内容,如下图:

获取过程:

打印方法结果:

怎么是**4**个方法?我写了两个方法**sayHello****sayBay**,为什么是四个方法,sayBay也没有打印,怎么回事?sayBay存储在哪?

由上图可看出:@property属性,由系统自动生成setget方法.类方法不存在这个类的方法列表中

三.总结

  1. 本篇文章主要分析了**isa和类,元类,根元类,根类之间的关系**,**bits如何获取**,着重分析了新的api下**类的结构**,**属性列表**,**方法列表存储**的位置.

  2. **遗留的问题** **成员变量**为什么没有找到,它存储在哪? **类方法**为什么不在类的方法列表中?它到底存储在什么地方?

知识点比较多,流程梳理比较耗时,做个笔记方便复习....