在我们的iOS开发中,经常会通过alloc去创建对象,调用alloc方法究竟做了什么?通过定位点击alloc方法,会进入到NSObject的alloc方法,但仅此而已,并不知道到实际做了什么?接下来我们通过3种方式窥探一下
一、3种方式
1、符号断点
<1>首先我们在ATPerson调用alloc的地方打个断点,运行代码,当断在alloc调用时,此时按住control键,点击Step into,可以看到调用objc_alloc了函数
<2>添加一个符号断点,选择Symbolic Breakpoint,输入objc_alloc,再次运行,可以看到调用了底层的libobjc.A.dylib`objc_alloc
2、通过汇编的方式
还是在alloc的位置打断点,运行,选择Debug-Debug Workflow-Always Show Disassembly
此时可以看到以汇编的模式看到调用堆栈信息,也可以看到调用objc_alloc
3、直接设置alloc符号断点
在[ATPerson alloc]加上断点,然后添加alloc符号断点,选择Symbolic Breakpoint,输入alloc, 添加完成先取消,然后运行,在走到[ATPerson alloc]时,打开alloc符号断点,此时可以看到调用底层的libobjc.A.dylib`+[NSObject alloc]:
通过以上3种方式可以知道alloc具体调用底层的什么方法
二、通过源码分析
首先可以打开opensource的官网下载对应的源码,本例以objc4-818.2版本说明
流程图
通过alloc方法定位到
+ (id)alloc {
return _objc_rootAlloc(self);
}
复制代码
然后进入_objc_rootAlloc
// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
复制代码
走到callAlloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
// slowpath: 慢速通道
// fastpath: 快速通道
// 根据编译器优化走哪个通道
if (slowpath(checkNil && !cls)) return nil;
// 是否有自定义的allocWithZone
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));
}
复制代码
重点代码
/***********************************************************************
* class_createInstance
* fixme
* Locking: none
*
* Note: this function has been carefully written so that the fastpath
* takes no branch.
**********************************************************************/
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
// 1、计算内存大小
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
// 2、开辟内存
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
// 3、绑定对象
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
复制代码
根据上面3点拆开分析
1、计算内存大小
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16; // 最低返回16字节
return size;
}
复制代码
进入cache.fastInstanceSize
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
} else {
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
// remove the FAST_CACHE_ALLOC_DELTA16 that was added
// by setFastInstanceSize
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
复制代码
align16方法以16字节对齐
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
复制代码
2、开辟内存
通过断点定位给obj分配内存空间,定义的id obj,在没有开辟内存空间之前查看也显示内存地址,此时是分配的脏内存(0x1002f6fc5)
继续往下走,根据之前计算的内存大小,执行calloc函数给对象开辟内存空间,此时obj赋有新的内存地址(0x100776020)
3、绑定对象
根据上面断点可以看到虽然给obj开辟了内存空间,但此时obj显示还是id类型,而没有关联到我们创建的对象上,接着可以看下面对象绑定的代码
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
复制代码
我们继续断点查看,调用obj->initInstanceIsa后,此时可以看到obj已经关联了我们创建的对象ATPerson
以上就是对alloc的底层分析初探