一、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对象的内存一样,仅仅是它们指针内存地址不同,由此可以得出结论
- 真正创建对象的是
alloc方法; init不会对指针做什么操作;- p1、p2和p3指针内存地址是连续的,他们的指向的是同一块内存地址。
如下图所示
那么
alloc是如何开辟内存的,我们通过代码跳转无法查看alloc具体内部实现,下方我们来分析底层遇到类似问题几种底层探究方法
二、底层探究三种方法
1.control键+step into查看调用流程
首先来到[LXPerson alloc]断点处,然后按照下方操作
最终跳转下面下方页面,会发现调用了objc_alloc方法
2.符号断点查看调用流程
继续调试发现无法继续下一步探究,下面我们可以添加objc_alloc的符号断点
继续下一步,我们发现来到了_objc_rootAllocWithZone方法
我们可以继续为_objc_rootAllocWithZone添加符号断点,继续下一步探究
3.汇编查看调用流程
代码运行到断点处后,打开Xcode的Debug->Debug Workflow->Alawys Show Disassembly
继续control键+step into,会调用到objc_alloc方法
然后添加符号断点,我们可以知道objc_alloc方法在哪个库里面
我们也可以直接添加alloc符号断点
既然我们知道了objc_alloc源码所在位置,我们在源码中调试,流程更为清晰
三、汇编结合源码调试分析
苹果objc4源码地址,我们用objc4-818.2分析源码,我们根据源码跳转,发现[LXPerson alloc]运行会调用NSObject的alloc方法,然后接着调用_objc_rootAlloc、callAlloc方法
接着添加alloc、_objc_rootAlloc和callAlloc符号断点,运行查看结果
根据上述流程,发现callAlloc没有被调用,_objc_rootAlloc后直接调用了objc_msgSend方法,这里是编译器优化后导致的,暂时不做过多展开
四、alloc调用主线流程
根据上面经过alloc、_objc_rootAlloc和callAlloc后,继续代码执行,会调用_objc_rootAllocWithZone
随后来到_class_createInstanceFromZone方法,下面详细分析里面3个重要步骤
1.cls->instanceSize计算需要内存空间大小
2.calloc申请内存空间
3.obj与当前cls关联
五、alloc详细流程图
根据前面分析,我们画出alloc大致实现流程图