前言:前面一片文章,我们对
alloc的源码进行了一些探究,不知道各位有没有发现一些问题,callAlloc为什么会走两遍。下面带着问题我们来分析
1、问题发现
alloc断点后 进入汇编查看 汇编代码 其实第一步走的并不是_objc_rootAlloc如图 汇编代码应该调用的是objc_alloc方法 而不是_objc_rootAlloc
2、通过源码分析
在alloc处打上断点 并查看堆栈信息 如下:
main 方法后 并非直接调用alloc 而是调用了一次
objc_alloc 这就和我们之前分析汇编吻合了
3、为什么第一次不是直接走alloc 而是走了objc_alloc呢?
通过代码来分析 第一次肯定是 调用了alloc方法,了解 sel 和imp的应该知道,有可能是在 main方法之前偷偷的把alloc 对应的imp 给改了呢 改成了objc_alloc 那我们就来找下 在加载dyld的时候 有没有进行一些操作 通过llvm源码来找
1.1 通过LLVM源码查找objc_alloc
如图:
基本可以确定的是 在调用alloc时候 是转成调用objc_alloc了
1.2分析objc_alloc
找到objc_alloc方法 再次运行 会调用callAlloc方法 在callAlloc 方法中断点 再次运行会走 objc_msgSend)(cls, @selector(alloc))方法 再次走到alloc 方法 然后再调用 _objc_rootAlloc -> callAlloc -> allocWithZone -> _objc_rootAllocWithZone -> createInstanceFromZone -> instanceSize -> initInstanceIsa -> return obj
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));
}
总结
整个alloc 流程 objc_alloc -> callAlloc -> objc_msgSend -> alloc-> _objc_rootAlloc -> callAlloc -> _objc_rootAllocWithZone ->_class_createInstanceFromZone -> cls->instanceSize -> calloc -> initinstanceIsa