在日常开发中我们经常使用到 alloc/init/new 来创建对象,那么它们具体有什么区别?这是今天要去探索的内容.
准备工作
objc源码下载:Apple Open Source,是苹果开源网站,选择 macOS,选择目前开源系统版本 找到对应得 objc 进行下载,此探索过程中我们用的是 macOS 10.15.1中的objc4-781.## alloc 探索
alloc 方法探索
1.在objc4-781中搜索 alloc
![](https://img.haomeiwen.com/i2928891/79695996b4b91ce9.jpg)
2.继续查看_objc_rootAlloc
![](https://img.haomeiwen.com/i2928891/716b8ffc4c3ba288.jpg)
3.继续查看callAlloc
![](https://img.haomeiwen.com/i2928891/8afc67e9c4ea5516.jpg)
(1). 在调用 callAlloc时,究竟是return _objc_rootAllocWithZone(cls, nil)还是objc_msgSend?在 Xcode 中打上他们的断点
![](https://img.haomeiwen.com/i2928891/ff02ce24c5539e5f.jpg)
通过断点调试,发现走的_objc_rootAllocWithZone方法
(2). 关于slowpath/fastpath说明下两个是objc 中定义的宏;
#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))__builtin_expect这个是啥玩意 咋不懂 度娘得知这个指令是gcc引入的,作用是允许程序员将最有可能执行的分支告诉编译器。这个指令的写法为:__builtin_expect(EXP, N)。意思是:EXP==N的概率很大,详情参考地址。
4.继续查看_objc_rootAllocWithZone
![](https://img.haomeiwen.com/i2928891/e42635fd0ee4918a.jpg)
5.继续查看_objc_rootAllocWithZone
![](https://img.haomeiwen.com/i2928891/fa4dc9d1e787c3ba.jpg)
6.到了这里,_objc_rootAllocWithZone出现了三个重要方法
(1). * instanceSize 计算要开辟多少内存
![](https://img.haomeiwen.com/i2928891/e983a69a0380b5c0.jpg)
跟踪instanceSize,最终发现它最终实现的是 align16方法;
![](https://img.haomeiwen.com/i2928891/2a8ae5e0434349e9.jpg)
这里进行16字节对齐,
了解下字节对齐:计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。对齐的作用和原因:如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。
(2). * calloc alloc 开辟内存的地方
![](https://img.haomeiwen.com/i2928891/46304236b10252e9.jpg)
obj = (id)calloc(1, size);在instanceSize中,已经计算了要开辟的内存大小size,calloc 向内存申请size 大小,并返回给 obj;如何证实呢,我们通过断点来验证,在控制台 po obj 看看前后值有什么区别在未执行 calloc 时,po obj 为 nil而在执行 calloc 后,po obj 返回一个16位地址
(3). * initInstanceIsa 将原来的类与开辟的内存绑定起来
![](https://img.haomeiwen.com/i2928891/30e42c7f26193e1c.jpg)
跟踪下去,最终实现了objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 方法.
在前面 内存已经计算好了,内存地址也已经申请了,通过initInstanceIsa将 obj 与 cls 关联起来.按照上面同样的方法,通过断点调试,在执行initInstanceIsa后 po obj 可以得到一个对象指针;
alloc 总结alloc 核心方法:计算内存大小-->申请内存--->关联绑定
init 探索
1.进入 init 方法
![](https://img.haomeiwen.com/i2928891/bc00e267c605bd57.jpg)
2.追踪_objc_rootInit
![](https://img.haomeiwen.com/i2928891/151edb708e111fa1.jpg)
这里可以看到 init 方法返回的是 id 对象本身;但是由于返回的是 id 类型的 self,给我们提供了自定义构造方法,通过类型转换实现了工厂设计,
new 方法探索
1.进入 new 方法
![](https://img.haomeiwen.com/i2928891/fbc407d4f886e153.jpg)
实际上就是调用了callAlloc,走的就是前面说的alloc流程。
最后的总结:alloc 流程图
![](https://img.haomeiwen.com/i2928891/7b45ac636f3b31a5.png)
网友评论