OC底层学习-alloc底层探索补充

366 阅读3分钟

alloc底层探究补充

在上一章 对alloc初探 中我们知道了 alloc实质就是为我们开辟内存空间并且关联我们的类,那我们有没有发现什么问题呢?这中间有没有啥我们还没发现的或者被忽略的呢?我们不妨再来走一遍alloc

先贴上新的alloc流程图

未命名文件(8).png

进入正题: image.png 通过debug-->debug workflow-->always show disaseembly查看汇编,

image.png 我们看到汇编代码callq --跳转--> objc_alloc ???

为啥不是alloc呢?

我们给allocobjc_alloc同时下好断点,看会先调用谁。。。 image.png image.png image.png 我们通过断点下一步,发现首先来到的是objc_alloc,好尴尬😓

alloc---->objc_alloc 什么鬼? 我们都知道sel---->imp,通过方法符号sel找到imp,然后再通过imp去找到相应的函数实现;那这儿到底是什么情况呢,我们可以猜想一下是不是sel对应的imp被修改了呢?

objc源码搜索objc_alloc

带着这个问题我们只有通过objc源码搜索objc_alloc了,一个一个的找

image.png 我们通过不断的查找objc_alloc会有个发现

image.png fixupMessageRef 字面意思是修复消息,那意思是当我们发送alloc消息转发时会被修复为objc_alloc?

我们继续跟踪搜索fixupMessageRef方法调用,发现在_read_images方法中会调用

image.png

image.png 其实我们应该可以在_read_images方法说明上发现调用_read_images的方法为map_images_nolock,那我们继续跟踪搜索map_images_nolock

image.png 继而又发现了map_images方法,那继续跟踪搜索map_images

image.png

我们对这个流程再正向来一次过后,在_read_images方法里去调用fixupMessageRef,而映射镜像的时候是发生在dyld之后的,那么在这么早的时候就开始sel-imp修复,说明在更早之前就开始imp绑定了,也就是LLVM编译阶段。


LLVM探索

LLVM源码

我么可以通过上面的链接下载到LLVM源码,通过VSCode打开源码,然后通过搜索objc_alloc

image.png 我们通过一个一个的探索,你会发现

image.png

if (Sel.isUnarySelector() && Sel.getNameForSlot(0) == "alloc")
          return CGF.EmitObjCAlloc(Receiver, CGF.ConvertType(ResultType));

如果sel是alloc然后就调用EmitObjCAlloc

image.png Apple这边会对一些特殊的方法例如alloc init等,像这些对系统内存直接操作的方法Apple是会做监控的,所以直接就hook住,就例如alloc方法,不会直接去找alloc,而是先objc_alloc下好标记,然后再去找alloc:

这就能接着刚开始的时候对allocobjc_alloc断点时先进入到objc_alloc,我们继续跟进去

image.png

image.png 来到这边过后,然后跟进去到callAlloc方法中时,我们下一步发现不会走_objc_rootAllocWithZone这个方法,而是通过下面的objc_msgSend重新对LhkhPerson发送了一个alloc消息

image.png 再次下一步过后则来到了alloc这个方法

为什么会是上面这种情况?

image.png

因为在第一次调用alloc的时候,LLVM会调用tryGenerateSpecializedMessageSend方法给alloc方法接收者标记,而当objc_alloc-->callAlloc-->objc_msgSend-->alloc这是后再来了一次alloc,LLVM已经对这个接收者标记了alloc方法,所以就会直接走的GenerateMessageSend方法,也就是直接去找alloc方法了。

通过上面的探索发现会出现这种情况,其实是LLVM编译时拦截优化,也就是当调用alloc方法的时候,LLVM会拦截,并通过方法给Receiver进行标记,当objc_alloc-->callAlloc-->objc_msgSend-->alloc后,LLVM发现已经标记了这个Receiver就会直接调用alloc;所以为什么callAlloc会走两次的原因也是这个。

alloc底层探究先到这,后续发现新问题在进行补充,内容有问题或者错误的希望大神能指正,相互学习😜!