iOS 底层原理探索 之 alloc
iOS 底层原理探索 之 alloc
写在前面: iOS底层原理探究是本人在平时的开发和学习中不断积累的一段进阶之
路的。 记录我的不断探索之旅,希望能有帮助到各位读者朋友。
内容的总结专栏
序
作为一名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的底层调用流程如下:
就是这样一个流程,系统就帮我们创建出来一个类对象。
补充
- lldb 如何打印实力对象中成员为 double 类型的数值: e -f f -- <值>