OC底层原理初探之alloc的探索下

1,938 阅读3分钟

前言

上文分析了alloc方法的流程,得出结论,alloc方法的调用流程为 alloc ->_objc_rootAlloc ->callAlloc ->_objc_rootAllocWithZone ->_class_createInstanceFromZone,最后在_class_createInstanceFromZone方法计算所需内存空间,开辟内存空间以及关联isa。本以为以及搞明白了alloc的流程。但是当在alloc方法打上断点,观察函数调用栈发现如下图

image.png

我们在XQPerson* person = [XQPerson alloc];处打上断点,然后查看汇编代码,也能够看到,显示为调用的objc_alloc

91642932141_.pic_hd.jpg

如函数调用栈中所示,在调用alloc方法还调用了objc_alloccallAlloc方法,所以完整的alloc流程应该是 objc_alloc->callAlloc->alloc ->_objc_rootAlloc ->callAlloc ->_objc_rootAllocWithZone ->_class_createInstanceFromZone

看到这里,不禁让人产生一个疑问,objc_alloccallAlloc是如何出现在 alloc 的调用流程里面的呢?带着这样的疑问,开始今天的探索。

首先需要确定,objc_alloc方法是在什么时候调用的,通过函数调用栈或者查看汇编代码都没鞥找到线索,只能在源码里全局搜索objc_alloc,最终在objc-runtime-new.mm文件发现了fixupMessageRef方法,将alloc的IMP替换成objc_alloc

image.png

接下来查找函数调用顺序,找出在什么情况下会进入替换alloc的IMP

全局搜索fixupMessageRef,发现在_read_images调用

image.png

通过_read_images函数的注释可知,函数的调用在map_images_nolock,我们搜索map_images_nolock可以发现,确实在此方法调用了_read_images

image.png

全局搜索map_images_nolock,发现此函数调用在map_images

image.png

全局搜索 map_images 发现此函数在 objc_init函数的_dyld_objc_notify_register,继续搜索这两个函数,找不到上一级调用了。到这里,fixupMessageRef函数调用流程锁定逆向查找就结束了。

通过上面的逆向查找可以确定,调用顺序为objc_init->_dyld_objc_notify_register->map_images->map_images_nolock->_read_images->fixupMessageRef 接下来通过在上面方法调用处依次打上断点调试

image.png

由上面的调试,发现fixupMessageRef并没有调用到,说明alloc被替换为objc_alloc并不是在fixupMessageRef。那么苹果为什么要提供一个可能并不会调用的修复函数呢?这里我们发散一下,真正的替换可能发生在编译阶段,而这里只是做了一个容错处理呢?

我们拿到LLVM的源码,拖拽到Visual Studio Code验证一下。

image.png

通过 MachOView验证可执行文件,也可以发现,在编译阶段已经存在 objc_alloc符号:

image.png

通过上面的分析可以得出,在llvm阶段已经完成了alloc->objc_alloc的替换,至于苹果为什么要hook这个函数,推测可能是系统对对象创建,释放等内存相关的函数进行了监控。

alloc完整流程图如下:

Untitled Diagram.drawio (1).png

流程总结: alloc 方法在编译阶段llvm会对其进行 hook,并将alloc 方法替换为 objc_alloc,当运行时创建一个XQPerson对象时,首先调用的是objc_alloc,然后进入callAlloc, 第一次判断if (fastpath(!cls->ISA()->hasCustomAWZ()))为假,执行((id(*)(**id**, SEL))objc_msgSend)(cls, @selector(alloc));方法,给XQPerson发送alloc消息,此时的调用流程 _objc_rootAlloc->callAlloc->_objc_rootAllocWithZone->_class_createInstanceFromZone此方法里面做三件事:计算所需空间大小开辟内存空间通过isa指针与类进行关联