alloc
方法是创建对象比不可少的步骤,我们平时一般都是直接使用alloc
创建对象的,那么alloc
的底层到底是怎么实现的?它是如何计算所需内存大小的?它是如何开辟内存空间的?它是怎么和类做关联的?带着这些问题看看alloc底层到底是怎么实现的?
准备工作
- 本文探索底层原理的主要方法是:源码、汇编、断点调试、LLDB调试等
- 苹果源码
- 本文示例代码地址(包含编译好的源码):gitee.com/jrcode/ios_…
alloc流程分析
定位源码位置
LGPerson *p1 = [LGPerson alloc];
复制代码
- 在上面代码位置下断点,执行到断点位置,按住
control+step into
,进入汇编
- 这里首先看到了调用了
objc_alloc
方法,但是很难再继续跟进流程,这种情况,我们可以使用符号断点 - 添加符号断点
objc_alloc
,并继续执行
- 从运行结果可以看出
objc_alloc
方法在libobjc.A.dylib
动态库中,即objc
源码- 接下来调用的是
_objc_rootAllocWithZone
和objc_msgSend
通过源码看alloc流程
- 上面已经定位到
objc_alloc
方法,在源码项目中直接搜索objc_alloc(
,定位到objc_alloc
方法实现的位置在NSObject.mm
文件中
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
复制代码
- 通过上面代码的注释也可以看出,调用
alloc
方法,实际上会调用objc_alloc
,然后再调用callAlloc
方法
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
复制代码
- 上面的方法中,没办法确定调用的是
_objc_rootAllocWithZone
还是objc_msgSend
,接下来该如何确定执行流程?- 方法一:因为我们调试的是可以运行的
objc
源码,所以可以直接在源码中添加断点,调试执行流程 - 方法二:汇编跟流程结合符号断点
- 注意:因为在系统初始化的时候,创建的对象也可能会调用到断点位置,所以需要先禁用断点,当执行到
**[LGPersion alloc]**
的断点处,再启用断点
- 方法一:因为我们调试的是可以运行的
动态调试
先使用方法二:汇编跟流程结合符号断点
- 先执行到
[LGPerson alloc]
的断点处,使用control+step into
进入汇编 - 下符号断点
objc_alloc
,然后继续执行到objc_alloc
- 单步向下执行,可以看到这里调用的是
objc_msgSend
,而不是_objc_rootAllocWithZone
- 执行到
objc_msgSend
,读取当前寄存器,可以看到,这里调用的是alloc
方法- register read:读取当前寄存器信息
- register read x0:x0寄存器中存放的是消息接收者
- register read x1:x1寄存器中存放的是SEL
- 从上述调试流程可以看出调用流程:
- objc_alloc
- msgSend
- alloc
再继续使用方法一:源码添加断点调试流程
- 先执行到
[LGPerson alloc]
的断点处,给下面源码处分别添加断点,再执行看流程
- 通过断点调试,可以确定源码调用流程:
- objc_alloc
- callAlloc
- return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
- alloc
- _objc_rootAlloc
- callAlloc
- _objc_rootAllocWithZone
- _class_createInstanceFromZone
- instanceSize:计算对象占内存大小
- calloc:开辟内存空间,并返回地址
- initInstanceIsa:初始化isa,和类做关联
- return obj; 返回对象地址
总结:alloc调用流程图
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END