OC-从alloc开始看底层

一、底层对象的alloc和init(new = alloc + init)

我们一般在创建对象时,都会通过[X alloc] init]这样的方式创建对象,但是allco的底层是如何调用的,init的作用是什么呢,我们来探索一下:

 

 

 当我们在通过 alloc 和init分开调用时可以发现,两次init的调用对于p1,p2来说,他们都是属于alloc创建出来的同一个对象,这是为什么呢?我们来通过汇编查找一下(汇编需要在真机上运行),我们在alloc的地方打个断点,同时勾选 code -> Debug -> Debug Workflow -> Always Show Disassembly 后运行。我们可以发现通过读取x1,x0 寄存器发现alloc已经创建好了对象,而没有见到init的身影。

小小补充一下:汇编中:b bl是跳转指令、ret 是函数的返回、另外寄存器一般保存返回的结果,还有运算器控制器等。

 

 

二、objc4-838源码分析alloc调用

 

 

我们通过对alloc不断跳转可以发现,callAlloc(带*的地方)执行了两次,第一次在执行了objc_msgSend方法,第二次执行了_objc_rootAllocWithZone方法,这是属于callAlloc的两种不同方法的执行分支(if),是否执行的判断条件在于fastpath(!cls->ISA()->hasCustomAWZ()) ,这个是判断cls是否被初始化以及是否自定义了allocWithZone方法,最后我们发现alloc在创建对象时是通过_class_createInstanceFromZone创建出来的,此时我们可以总结出alloc的底层实现流程:(alloc最终返回 一个obj)

 

_class_createInstanceFromZone 这个方法可能会被编译器优化掉。

 三 init的作用(new)

我们从objc的源码中可以看出init方法并没有做任何实质性的操作,就是一个工厂模式,可以让我们重写这个方法来初始化一些自定义的操作。

 

 

四、alloc的内存申请

在这里我们需要先知道一点,在64位的操作系统中,内存是按照8位对齐的,在oc中,对象是一个objc_object的结构体,存储的是isa+成员变量的值。

接下来我们看一下_class_createInstanceFromZone的内部是如何操作的

 

看代码if (size < 16) size = 16 可以发现通过allo或者new创建出来的对象最小的大小是16个字节,_class_createInstanceFromZone只是计算对象开辟需要的内存大小,系统实际为对象分配的内存大小依赖的是calloc函数,

 

 而calloc函数是是以16字节对齐的,那么这里就有问题了。为什么为对象开辟的空间是8字节,而存储的时候是16字节呢?,我们接着看_class_createInstanceFromZone方法

 

 我们可以看到这里执行了一个initInstanceIsa方法,其实这里是把calloc返回的内存地址关联到了相应的类上,由此我们可以发现_class_createInstanceFromZone方法内部主要做了以下内容:

 

 五 对象的对齐

我们刚刚已经知道了对象是一个objc_object的结构体,存储的是isa+成员变量的值,那么我们需要解释以下两点:

1.为什么需要字节对齐

其实字节对齐的根本就是为了加快CPU访问的速度,这就是一种以空间换时间的方式,,我们都知道字节是内存读取的单位,但CPU读取的单位却是块,我们通过字节对齐的方式来形成块,加块CPU读取的速度。

2.为什么对象的成员变量以8字节对齐,而系统实际分配的内存是以16字节对齐呢?

OC的对象中,有一个isa指针,占了8位字节,就算对象中没有其他的属性了,也⼀定有⼀个isa,那对象就⾄少要占⽤8位字节。如果以8位字节对⻬的话,如果连续的两块内存都是没有属性的对象,那么它们的内存空间就会完全的挨在⼀起,是容易混乱的。以16字节为⼀块,这就保证了CPU在读取的时候,按照块读取就可以,效率更⾼,同时还不容易混乱。
 
六、结构体对齐(这部分在swift里已经讲过了 这里我们以几个例子来说明即可)
struct LGStruct1 {
    //存储准则,需要是当前这个变量占据字节长度的整数倍
    double a;//double占8个字节,则存储在0~7
    char b;//char是1字节 存储在8
    int c;//int是4字节 存储在12~15
    short d;//short是2字节,存储在 16~17
}struct1;//总大小是内部最大成员的整数倍,所以是24

struct LGStruct2 {
    double a;//double占8个字节,则存储在0~7
    int b;//int是4字节 存储在8~11
    char c;//char是1字节 存储在12
    short d;//short是2字节,存储在 14~15
}struct2;//总大小是内部最大成员的整数倍,所以是16

struct LGStruct3 {
    double a;//double占8个字节,则存储在0~7
    int b;//int是4字节 存储在8~11
    char c;//char是1字节 存储在12
    short d;//short是2字节,存储在 14~15
    int e;//int是4字节 存储在16~19
    struct LGStruct1 str;//从内部最大元素大小的整数倍开始存储,24开始存储 24~41
}struct3;//总大小必须为内部最大成员的整数倍,所以是8的整数倍,所以struct的大小为48

对齐原则按照下面的方式对齐:

1 数据成员对齐规则:结构体的第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置从该成员的整数倍开始存储

2.结构体作为其他结构体中的一个成员时,则从该这个结构体中内部最大元素的整数倍地址开始存储

3.结构体的总大小,其大小就是其中最大成员的整数倍


 

 

posted on 2022-04-18 22:00  suanningmeng98  阅读(55)  评论(0编辑  收藏  举报