序
作为一名iOS开发人员,在平时开发工作中,所有的对象我们使用最多的是alloc来创建。那么alloc底层做了哪些操作呢?接下来我会一步一步探究alloc方法的底层实现。
初探
我们先来看下面的代码
SMPerson *p1 = [SMPerson alloc];
SMPerson *p2 = [p1 init];
SMPerson *p3 = [p1 init];
NSLog(@"%@-%p-%p", p1, p1, &p1);
NSLog(@"%@-%p-%p", p2, p2, &p2);
NSLog(@"%@-%p-%p", p3, p3, &p3);
复制代码
打印内容:
<SMPerson: 0x600000710400>-0x600000710400-0x7ffee6f15088
<SMPerson: 0x600000710400>-0x600000710400-0x7ffee6f15080
<SMPerson: 0x600000710400>-0x600000710400-0x7ffee6f15078
复制代码
可见,在 SMPerson 使用 alloc 方法从系统中申请开辟内存空间后 init方法并没有对内存空间做任何的处理,地址指针的创建来自于 alloc方法。如下所示:

注:细心的你一定注意到了,p1、p2、p3都是相差了8个字节。 这是因为,指针占内存空间大小为8字节,p1、p2、p3 都是从栈内存空间上申请的,且栈内存空间是连续的。同时,他们都指向了同一个内存地址。
那么, alloc 是如何开辟内存空间的呢?
首先,第一反应是,我们要Jump to Definition,

结果,Xcode中并不能直接跳转后显示其底层实现,所以 并不是我们想要的。


中探
接下来,我们通过三种方法来一探究竟:
方法1
既然不可以直接跳转到API文档来查看alloc的内部实现,那么我们还可以通过下 符号断点 来探寻 其实现原理。

接下来我们就来到此处

一个名为 libobjc.A.dylib 的库,至此,我们就应该要去找苹果开源的库,以寻找我们想要的答案。
方法2
我们也可以直接在alloc那一行打一个断点,代码运行到此处后,按住control键 点击 step into, 接下来,就来到里这里

我们可以看到一个 objc_alloc 的函数方法到调用,此时,我们再下一个符号断点,同样的,我们还是找到了 libobjc.A.dylib 这个库。

方法3
此外,我们还是可以通过汇编来调试和查找相应的实现内容,断点依然是在alloc那一行。
Debug > Debug Workflow > Always Show Disassembly

找到 callq 方法调用那一行,

接着, step into 进去, 我们找到了 objc_alloc 的调用, 之后的操作和 方法2的后续步骤一样,最终,可以找到 libobjc.A.dylib 这个库。

深探
下载源码 objc4-818.2
接下来对源码进行分析,
alloc方法会调用到此处

接着是 调用 _objc_rootAlloc

之后调用 到 callAlloc

跟着断点会来到 _objc_rootAllocWithZone

之后是 _class_createInstanceFromZone
此方法是重点

_class_createInstanceFromZone 方法中,该方法就是一个类初始化所走的流程,重点的地方有三处
第一处是:
// 计算出开辟内存空间大小
size = cls->instanceSize(extraBytes);
复制代码
内部实现如下:

其中在计算内存空间大小时,会调用 cache.fastInstanceSize(extraBytes) 方法,
最终会调用 align16(size + extra - FAST_CACHE_ALLOC_DELTA16) 方法。
align16 的实现如下:
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
复制代码
可见, 系统会进行 16字节 的对齐操作,也就是说,一个对象所占用的内存大小至少是16字节。
在这里 我们举个例子: size_t x = 8; 那么 align16操作后的大小计算过程如下:
(8 + 15) & ~15;
0000 0000 0000 1000 8
0000 0000 0000 1111 15
= 0000 0000 0001 0111 23
1111 1111 1111 0000 ~15
= 0000 0000 0001 0000 16
复制代码
第二处是:
///向系统申请开辟内存空间,返回地址指针;
obj = (id)calloc(1, size);
复制代码
第三处是:
/// 将类和指针做绑定
obj->initInstanceIsa(cls, hasCxxDtor);
复制代码
所以,最后我们总结一下, alloc的底层调用流程如下:

就是这样一个流程,系统就帮我们创建出来一个类对象。
























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)