iOS底层探索--类的结构探索

1,120 阅读5分钟

小谷底层探索合集

  • 今天来探索一波,这个东西大家都不陌生,不过今天我们去里面看看他的结构是啥样子的!
  • 我们会通过isa的走向类的继承内存偏移3个方面深入探索类结构
  • 我们研究的是目前最新的源码 objc4-781

1. isa的走向

1.1. isa存在的证明

    1. 对于isa大家应该也不陌生,我们今天证明下,isa的存在!
    1. 我们定义一个
@interface XGClass : NSObject
@end
    1. 我们通过clang把文件编译成C++后,我们看到了个东东:
#ifndef _REWRITER_typedef_XGClass
#define _REWRITER_typedef_XGClass
typedef struct objc_object XGClass;//兄弟们!!所以在底层C++中,类对象是通过结构体objc_object为模板创建的!!
typedef struct {} _objc_exc_XGClass;
#endif


struct XGClass_IMPL {
     struct NSObject_IMPL NSObject_IVARS;
};
    1. 我们可以查看源码:
typedef struct objc_class *Class;//类信息

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
    1. 我们可以看出,所以的类对象都有isa

1.2. 分析isa的指向

  • 在探索过程中可能会涉及到一个元类的内容,元类是由编译器自动创建,是类的归属! , 不慌。我们一步步探索。

  • 1、 对于isa大家应该都不陌生。我们可以通过isa拿到类信息

  • 2、我们看下类的内存分布

  • 3、我们发现:类里面的确也存储了一些东西。首地址存储的还是isa,然后查看isa存的啥

  • 4、发现也是存的 XGClass ,这个就是元类!!

  • 5、我们继续探索,研究isa的指向:难道他最终指向元类吗?还是说他没有结束?

  • 6、我们发现指向元类之后,isa指向的类信息都是一样的!!所以我们得出一个结论图!如下:

2. 类的继承

  • 相信大家对于继承并不陌生!继承简单来说就是(谁是谁的儿?谁是谁的爹😆)

  • 这个我就不多说了!(毕竟我觉得看我博客的兄弟们除了帅之外也很聪明)

  • 在下又偷了一波图(表面上:偷图使人快乐!!内心想法:怎么谁都比我画的好)

  • 通过isa的指向图继承关系图,我们就可以合成一个炒鸡经典的🐍皮走位图(大名叫:isa流程图

  • 好啦,继承关系大家应该知道了,那么我们继续!!

3. 内存偏移

内存偏移本来我是不想写在博客中了,感觉大家这么聪明,还用我来证明什么?,哎,奈何在下天生愚昧,就当给自己解释一波了

  • 我们先举例说明,然后在举例过程中中了解内存偏移

3.1. 普通指针

int a = 10;
int b = 10;
NSLog(@"a的地址是:%p   a的值是:%d",&a,a);
NSLog(@"b的地址是:%p   b的值是:%d",&b,b);

打印结果:

  • a、b地址相差4个字节。这个取决于a、b的类型(int),然后a、b的值相同地址不同,说明他们都指向10这个常量!

  • 来附图说明(这个可不是偷的!!😆):

3.2. 对象指针

  • 这个大家一直在用(不过可能当成规定用了,😆)
XGStudent *student1 = [XGStudent alloc];
XGStudent *student2 = [XGStudent alloc];
    
NSLog(@"%@ --- %p",student1,&student1);
NSLog(@"%@ --- %p",student2,&student2);
  • 打印结果:

  • student1student2 指向[XGStudent alloc]的内存地址!

  • &student1也就是取指向student1的指针地址,也就是二级指针的地址!

  • 来图(又要发挥我画家的天赋了):

3.3. 数组指针

  • 这个就比较好明白了,毕竟C语言中有定义
    //声明数组
    int a[5] = {0,1,2,3,4};
    //一个整型的指针
    int *p;
    //指向数组a的首地址
    p = a;
    
    NSLog(@"p指针指向元素:%d,a数组的第一个元素:%d",*p,a[0]);
    NSLog(@"p指针偏移一位:%d,a数组的第二个元素:%d",*(p+1),a[1]);
  • 输出结果:

  • 画幅图让大家更清晰一步 这就传说中的内存偏移

4. 探索类

  • OK,已经说完isa的指向类的继承关系内存偏移了,终于可以进入正题了!!

4.1. 分析类结构

  • 根据上面我们可以找到这个类的结构,我这里就直接粘出来了!
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);
    }

//下面代码太长,无关,省略一波。
}

其实刚看到这么一大串代码的时候,都会懵逼。就很难受,最恐怖的是啥注释没有!自己英文也不好

  • 我们第一眼看下去,不知道这个是啥,然后我们简单的理解下!

  • 第一个是isa(这个都知道了)

  • 第二个是Class

  • 第三个cache_t,不知道这个是啥(没关系,探索就是认知的过程,刚开始谁都不知道)

  • 第四个class_data_bits_t,我也不知道。但是下面这个set、get方法咋操作他了。。诶,这个时候,我就感觉,这个里面有点东西!!(那我们就看看他)

我们可以通过内存偏移。找到这个东西!

4.1.1. 探索cache_t

  • OK,我们来找找:bits

  • 首先:isa 8个字节, class 8个字节,cache这TM是什么东西(完了,又卡主了,这个时候点进去看看)

  • 应为cache是个结构体不是结构体指针。不明白的话可以看我另一篇博客: 内存对齐

4.2. 获取data

  • 既然我们知道他要怎么偏移,所以我们可以通过偏移32字节来锁定bits,拿到他!!

  • 兄弟们看我操作!!

  • 有些兄弟会一脸懵逼(这在干啥?),我非常贴心的画了一幅流程图😆

  • 大家可以通过data拿到很多信息,可以点进class_rw_t来看看,当然也有好多看不懂的,没关系。我们挑看的懂的来就行:

这个大家有兴趣的话可以探索一波

OK!结语了! 毕竟这篇博客写的有点长了,写太多的话,我怕大家烦躁,希望对大家有帮助吧!!(看我有苦又累的情况下,有条件的可以点个赞,哈哈😆)