alloc底层原理探究

1,078 阅读2分钟

一、alloc对象的指针和内存地址

新建一个LXPerson类,运行起来查看下方代码运行结果

LXPerson *p1 = [LXPerson alloc];
LXPerson *p2 = [p1 init];
LXPerson *p3 = [p1 init];

NSLog(@"%@-%p-%p", p1, p1, &p1);
NSLog(@"%@-%p-%p", p2, p2, &p2);
NSLog(@"%@-%p-%p", p3, p3, &p3);

代码运行结果如下:

<LXPerson: 0x10050f7c0>-0x10050f7c0-0x7ffeefbff3e8
<LXPerson: 0x10050f7c0>-0x10050f7c0-0x7ffeefbff3e0
<LXPerson: 0x10050f7c0>-0x10050f7c0-0x7ffeefbff3d8

我们发现p1、p2和p3对象的内存一样,仅仅是它们指针内存地址不同,由此可以得出结论

  1. 真正创建对象的是alloc方法;
  2. init不会对指针做什么操作;
  3. p1、p2和p3指针内存地址是连续的,他们的指向的是同一块内存地址。

如下图所示

image.png

那么alloc是如何开辟内存的,我们通过代码跳转无法查看alloc具体内部实现,下方我们来分析底层遇到类似问题几种底层探究方法

二、底层探究三种方法

1.control键+step into查看调用流程

首先来到[LXPerson alloc]断点处,然后按照下方操作

image.png

最终跳转下面下方页面,会发现调用了objc_alloc方法

image.png

2.符号断点查看调用流程

继续调试发现无法继续下一步探究,下面我们可以添加objc_alloc的符号断点

image.png

image.png

继续下一步,我们发现来到了_objc_rootAllocWithZone方法

image.png

我们可以继续为_objc_rootAllocWithZone添加符号断点,继续下一步探究

3.汇编查看调用流程

代码运行到断点处后,打开Xcode的Debug->Debug Workflow->Alawys Show Disassembly

image.png

image.png

继续control键+step into,会调用到objc_alloc方法

image.png

然后添加符号断点,我们可以知道objc_alloc方法在哪个库里面

image.png

我们也可以直接添加alloc符号断点

image.png

既然我们知道了objc_alloc源码所在位置,我们在源码中调试,流程更为清晰

三、汇编结合源码调试分析

苹果objc4源码地址,我们用objc4-818.2分析源码,我们根据源码跳转,发现[LXPerson alloc]运行会调用NSObject的alloc方法,然后接着调用_objc_rootAlloccallAlloc方法 image.png

image.png

image.png

接着添加alloc_objc_rootAlloccallAlloc符号断点,运行查看结果

image.png

image.png

image.png

根据上述流程,发现callAlloc没有被调用,_objc_rootAlloc后直接调用了objc_msgSend方法,这里是编译器优化后导致的,暂时不做过多展开

四、alloc调用主线流程

根据上面经过alloc_objc_rootAlloccallAlloc后,继续代码执行,会调用_objc_rootAllocWithZone

image.png

随后来到_class_createInstanceFromZone方法,下面详细分析里面3个重要步骤

1.cls->instanceSize计算需要内存空间大小

image.png

2.calloc申请内存空间

image.png

3.obj与当前cls关联

image.png

五、alloc详细流程图

根据前面分析,我们画出alloc大致实现流程图

image.png