在以前分析源码时,都是在OpenSource直接下载,然后全局搜索分析源码调用,在最新的objc781源码下载后,我们进行了可编译调试配置,调试步骤可以参考月神的:objc4-781 源码编译 & 调试
alloc源码调用流程
最新的alloc调用从源码上看和以前的差不多
现在我们使用可调式源码来分析下这个流程是否正确。
我们在可调试源码中添加一个target,并设置objc依赖,如下所示
在main.h
添加代码如下:
#import <Foundation/Foundation.h>
#import "LWClassTest.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj1 = [NSObject alloc];
LWClassTest *obj2 = [LWClassTest alloc];
NSLog(@"Hello, World!");
}
return 0;
}
我们在obj1生成处打一个断点,并设置Debug->Debug Workflow->Always Show Disassembly
来查看汇编中的调用
我们看到,在[NSObject alloc]
时,调用的是objc_alloc
,而并不是我们以为的_objc_rootAlloc
,这和我们之前研究源码的认知好像不一样,这是为什么呢?
为什么调用objc_alloc
?
-
首先,我们在main中obj1和obj2行都打上断点,并在源码
objc_alloc
处打上断点,运行前暂时让这里的断点disable,如下图所示
-
运行,在main中停下时,让
objc_alloc
处断点enable,继续运行,我们可以看到停在了objc_alloc
处的断点,源码中可以看到,下一步调用的是callAlloc
,再在callAlloc
中打上断点
继续运行,我们发现它会走到1714行,调用消息发送,给类对象发送一个
alloc
消息。再继续运行,它就会走到我们一开始介绍的alloc执行流程。
走到这里,我们发现,虽然验证了中间要走objc_alloc
流程,但是为什么会走我们还是没有解释到。
在以前的经验中,我们知道,如果某个方法,如果没有走到它的方法实现,那么它很可能被hook了,这里也是一样的道理,我们的系统在进行alloc时,会对它进行拦截,这部分我们可以看LLVM
源码中的实现。
-
在llvm源码中搜索objc_alloc
-
搜索
shouldUseRuntimeFunctionForCombinedAllocInit
,表示版本控制
-
搜索
tryEmitSpecializedAllocInit
,非常著名的特殊消息发送,在这里也没有找到objc_alloc
-
继续尝试,开启上帝视角,通过
alloc
字符串搜索,如果还找不到,还可以通过omf_alloc:
找到tryGenerateSpecializedMessageSend
,表示尝试生成特殊消息发送
-
然后在这个case中可以找到调用alloc,转而调用了objc_objc的逻辑,其中的关键代码是EmitObjCAlloc
-
跳转至
EmitObjCAlloc
的定义可以看到alloc
的处理是调用了objc_alloc
由此可以得出
NSObject
中的alloc
会走到objc_alloc
,其实这部分是由系统级别的消息处理逻辑,所以NSObject
的初始化是由系统完成的,因此也不会走到alloc
的源码工程中
alloc实际调用流程
所以,在调用alloc
方法时,实际的调用流程为: