alloc初探

一、alloc的作用

作为oc语言开发者,大家都知道如何创建一个实例对象,没错就是alloc。那么alloc在底层是如何调用的呢?我们一起来探究一下。
代码搞起

    LGPersion *p = [LGPersion alloc];
    LGPersion *p1 = [p init];
    LGPersion *p2 = [p init];
    
    NSLog(@"p : %@---%p----%p",p,p,&p);
    NSLog(@"p1: %@---%p----%p",p1,p1,&p1);
    NSLog(@"p2: %@---%p----%p",p2,p2,&p2);
复制代码

打印结果

p : <LGPersion: 0x6000006c0160>---0x6000006c0160----0x7ffee9ae3c58
p1: <LGPersion: 0x6000006c0160>---0x6000006c0160----0x7ffee9ae3c50
p2: <LGPersion: 0x6000006c0160>---0x6000006c0160----0x7ffee9ae3c48
复制代码

通过上述代码和打印结果可以明显看出:p,p1,p2三者的对象地址是一样的,但是指针地址不一样,所以p,p1,p2三个对象指向的是同一个内存空间,在内存中的表现如图所示:

未命名文件.png

结论:alloc开辟了对象的内存空间,init并没有对空间有操作

二、探究alloc的方法

然而alloc是如何开辟内存的呢?我们该如何去探究呢?接下来我们用3种方法去探索:

1、setp into + 符号断点

先在alloc之前加个断点,然后按住control 点击setp into

截屏2021-06-06 下午10.02.58.png
进入

截屏2021-06-06 下午10.05.02.png
然后加入符号断点objc_alloc

截屏2021-06-06 下午10.07.34.png
放开断点进入libobjc.A.dylib objc_alloc:

截屏2021-06-06 下午10.07.44.png

2、汇编

汇编模式:debug -> debug Workflow ->Always Show Disassembly
具体使用:进入断点后按照上述步骤即可进入汇编代码。如图:

1、断点

截屏2021-06-06 下午9.25.14.png

2、进入汇编

截屏2021-06-06 下午9.12.02.png
汇编:

截屏2021-06-06 下午9.26.47.png

3、step into

截屏2021-06-06 下午9.28.17.png
按住control 点击step into 进入alloc

截屏2021-06-06 下午9.28.37.png

3、直接符号断点

我们都知道这个方法是alloc
所以直接下符号断点,流程如下:
先在alloc前打断点断点之后如图

截屏2021-06-06 下午9.42.17.png

截屏2021-06-06 下午9.42.35.png
加入符号之后直接放开当前断点

放开.jpg
然后如图所示

截屏2021-06-06 下午9.41.12.png

三、alloc源码跟踪

打开Source Browser 搜索objc,如图:

截屏2021-06-06 下午10.20.52.png

下载objc4-818.2.tar.gz解压

command + 左键alloc

直接跳转到

截屏2021-06-06 下午10.27.40.png

继续 command

截屏2021-06-06 下午10.28.30.png
继续

截屏2021-06-06 下午10.30.32.png
进入_objc_rootAllocWithZone

继续进入_class_createInstanceFromZone (核心代码)

核心.jpg

如图所示(已经标出主要部分)

cls->instanceSize(extraBytes) 计算大小

calloc(1, size) 开辟空间

initInstanceIsa(cls, hasCxxDtor) 将开辟的空间跟类关联起来

细细一品确实是核心东西 ,那么下面开始挨个探究。

1、计算大小

我们首先进入instanceSize
如图

大小计算.jpg
有个判断先,这里判是否有缓存 有的话直接获取返回(节省时间)

进入cache.fastInstanceSize

截屏2021-06-06 下午11.15.55.png

command 查看FAST_CACHE_ALLOC_MASK 值为0x1ff8

如图点断所示,我们需要进入align16

截屏2021-06-06 下午11.21.08.png

梳理一下这个算式

设x = 9 
则算式为 (9 + 15) & ~ 15
二进制计算
x = 0000 1001
15 : 0000 1111
9 + 15 : 0001 1000
~15 :1111 0000
9 + 15) & ~ 15 : 0001 0000 结果为16

设x = 17
则算式为 (17 + 15) & ~ 15
二进制计算
x = 0001 1000
15 : 0000 1111
9 + 15 : 0010 0111
~15 :1111 0000
9 + 15) & ~ 15 : 0010 0000 32

复制代码

所以 align16 是一个对齐方法,即返回16的整数倍,如果不足16,返回16,如果x>16*n && x<16 * (n + 1)则返回 16*(n +1),等同于>> 4 <<4,该方法叫做字节对齐,字节对齐的意义是什么?

提高cpu读取速度:以固定的长度读取数据,会大幅度提高cup工作效率

接下来查看 普通方法 command 进入alignedInstanceSize

截屏2021-06-06 下午11.51.27.png
继续进入unalignedInstanceSize(没有对齐的InstanceSize

截屏2021-06-06 下午11.54.35.png

所以此方法返回的是对象的大小(未对齐之前的),那么对象的大小如何判断的呢?我们细想一下,对象里面有什么?属性、方法、和协议等;但是对象的大小只取决于成员变量。该方法返回的是data中的roinstanceSize,即实例变量的大小。该方法的注释中也提到 May be unaligned depending on class's ivars.成员变量的大小。

然后 word_align

截屏2021-06-07 上午12.04.36.png
WORD_MASK 为7
所以该方法是8字节对齐方法。

最后返回到 instanceSize 中返回

2、开辟空间

截屏2021-06-07 上午12.21.05.png
此图断点为刚声明,虽然有地址,却是脏地址,不可用的。

截屏2021-06-07 上午12.21.17.png
截屏2021-06-07 上午12.13.53.png

越过开辟的方法,之后发现地址变了,但是po一下obj发现只是一个地址,跟平时的对象不太一样

截屏2021-06-07 上午12.23.11.png
因为接下来的步骤还没走,class绑定

3、绑定

进入 initInstanceIsa

截屏2021-06-07 上午12.25.39.png

进入 initIsa

截屏2021-06-07 上午12.34.24.png

完事了,我们回到 _class_createInstanceFromZone 并po,发现obj正常了,跟平时的一样,说明我的alloc走完了。

截屏2021-06-07 上午12.36.04.png

4、总结

通过以上的探索在下绘制了,简单的alloc流程图:

未命名文件-2.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享