一、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大致实现流程图