- 今天来探索一波
类
,这个东西大家都不陌生,不过今天我们去里面看看他的结构是啥样子的! - 我们会通过
isa的走向
、类的继承
和内存偏移
3个方面深入探索类结构
- 我们研究的是目前最新的源码
objc4-781
1. isa的走向
1.1. isa存在的证明
-
- 对于
isa
大家应该也不陌生,我们今天证明下,isa
的存在!
- 对于
-
- 我们定义一个
@interface XGClass : NSObject
@end
-
- 我们通过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;
};
-
- 我们可以查看源码:
typedef struct objc_class *Class;//类信息
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
-
- 我们可以看出,所以的类对象都有
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);
-
打印结果:
-
student1
和student2
指向[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!结语了! 毕竟这篇博客写的有点长了,写太多的话,我怕大家烦躁,希望对大家有帮助吧!!(看我有苦又累的情况下,有条件的可以点个赞,哈哈😆)