对象原理 --- 调试方式和alloc流程分析

412 阅读3分钟

一、调试方法

当我们调试时候不知道一个系统方法后面干了些啥,调用了哪些动态库的时候,我们可以通过下断点方式进行调试,有三种方式

1、断点调试

在调用系统方法前加一个断点,例如alloc方法,然后按住control键,点击in键,也就是

image.png

这个键,一步一步跳转就能看到该方法调用的顺序,例如

image.png

就能知道alloc调用了objc_alloc方法

2、下符号断点方式

在symbolic 里面symbol里面写入已知的系统方法,例如

image.png

然后就会出现下面这种情况

image.png

说明拥有alloc系统方法的类很多,然后我们在main函数中创建对象就会拦截到

image.png

3、通过汇编 :

Debug ---> Debug workflow ----> allways show,然后在系统方法前加一个断点后,就会出现这种情况

image.png

会把该方法的前后所有调用顺序都展现出来,然后再配合断点来进行调试

二、alloc流程分析

我们查找下alloc的调用流程,授信我们从官网下载objc4-750的源码,通过搜索 alloc { 我们可以发现

image.png

alloc其实是调用_objc_rootAlloc函数,再搜索_objc_rootAlloc函数

image.png

会发现_objc_rootAlloc调用的是callAlloc方法,再搜索callAlloc函数

image.png

然后一步一步分析首先看第一个obj对象

image.png

然后查看calloc函数

image.png

其中calloc函数意思是开辟一个内存空间,cls->bits.fastInstanceSize()意思是开辟一个cls类的内存空间的大小,前面__count意思是倍数,其中cls->bits.fastInstanceSize()大小是遵循内存对齐原则开辟内存的

内存对齐原则:

  • 1、数据成员对⻬规则:结构体(struct)(或联合体(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。

  • 2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储.)

  • 3、收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬。

内存对齐原则注意点

  • 1、对象的属性需要的内存空间是8位倍数,如果不以8的倍数,CPU读起来很a耗性能高,所以以8字节为倍数,提高读取效率,以空间换时间

  • 2、开辟的对象的空间如果小于16字节的分配16个字节,防止越界

我们再继续分析代码,由于calloc不在objc源码里面,是在malloc源码里面,我们下载malloc源码,查找下calloc的实现

image.png

然后进入到 retval = malloc_zone_calloc(default_zone, num_items, size); 方法里面

image.png

这样就开辟除了一块内存空间,然后通过callBadAllocHandler将这块内存空间和cls类关联起来

image.png

如果开辟不成功就会调用initInstanceIsa方法去

image.png

再分析这一块

image.png

首先查看class_createInstance函数的实现

image.png

然后再查看_class_createInstanceFromZone函数的实现

image.png

当满足条件时候

image.png

先给objc开辟一个内存空间,然后将类cls的isa指针给obj,我们查看下initInstanceIsa的实现

image.png

然后查看initIsa函数实现

image.png