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字节对齐呢?
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.结构体的总大小,其大小就是其中最大成员的整数倍