OC底层探索方式
在探索alloc底层实现之前,我们要掌握OC底层探索的三种方式:
一、通过下符号断点的方式确定相关底层函数的调用
以探索alloc为例:
1、在对象alloc这一行打一个断点,运行程序,来到断点位置
2、按住ctrl键,单击step info,我们可以直接来到objc_alloc方法
3、添加objc_alloc符号断点,我们就可以看到objc_alloc的实现方法了
二、通过汇编的方式(常用、推荐)
重复上面1步骤,我们来到断点后,点击Debug -- Debug Workflow -- Always Show Disassembly,就可以直接来到汇编代码,并确定当前底层执行的symbol(方法/函数)。再添加符号断点就可以看到objc_alloc的实现
三、通过已知的符号断点,依次确认未知的符号
我们可以直接下一个"alloc"符号断点,就可以来到libobjc.A.dylib`+[NSObject alloc]
通过objc源码探索alloc的底层实现
objc源码地址可以在opensource上找到并下载 打开objc源码,我们直接搜索alloc,可以看到:
+ (id)alloc {
return _objc_rootAlloc(self);
}
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
接下来我们直接通过断点调试,来一步一步跟踪alloc的流程
第一步,进入到alloc方法,之后执行_objc_rootAlloc
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
接下来,来到了callAlloc方法
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));
}
这个方法里面,先有个nil的判断(通过参数直接判断),接下来会通过fastpath(!cls->ISA()->hasCustomAWZ())方法判断当前对象是否有自定义的allocWithZone方法,如果没有直接走_objc_rootAllocWithZone方法,有的话就通过objc_msgSend执行对象的alloc方法。通过调试,发现直接走到了_objc_rootAllocWithZone方法
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
直接来到_class_createInstanceFromZone方法
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;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
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;
}
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);
}
主要有三个关键方法 cls->instanceSize这个方法是计算对象所需要的内存大小(这里面有个16字节对齐处理) cls->calloc这个方法主要是为对象申请开辟内存空间 cls->initInstanceIsa这个方法的主要作用是通过isa,将obj与cls进行关联 通过这些流程之后,我们的obj就被alloc出来了。
备注:这里面还有个分支流程需要后续补充,即:callAlloc方法中,cls若实现了allocWithZone方法的流程。