在编译时,发现两个问题:
问题1. NSObject的alloc方法不走常规路
问题2. 自定义类HTTest的alloc方法进入callAlloc被调用2次
发现路径:
问题1:NSObject的alloc方法不走常规路
我们在[NSObject alloc]入口和alloc类方法打断点:


发现未进入alloc类方法。于是在图一断点时,打开汇编模式

发现下一个执行函数是objc_alloc

于是我们全局搜索objc_alloc函数。在NSObject.mm文件中找到objc_alloc函数,加断点检测。

发现此时cls刚好是NSObject。

接下来调用callAlloc函数,回归正常对象的alloc流程

新问题:NSObject是如何忽略alloc,直接调用objc_alloc函数的呢?
这里需要通过llvm源码来探究。 源码1G多,这里我展示一下就行。实际上NSObject是系统自动创建和管理的。
- 在
VSCode中打开llvm源码文件,搜索alloc,找到CGObjC.cpp文件夹。

- 可以看到这里有明确标注,
[self alloc] -> objc_alloc(self) - 函数中显示,当接收到
alloc名称的selector时,调用EmitObjCAlloc函数。我们继续搜索EmitObjCAlloc:

- 在这里,我们看到
发射了一个objc_alloc消息 - 到这里,我们已经知道
objc4源码中objc_alloc信号是从这发出的。 - 具体的发送机制,后续课程会深入了解。 现在我们只要知道,
NSObject的alloc是系统在llvm底层帮我们转发到objc_alloc。LLVM在我们编译启动时,就已经处理好了 👍
问题2:自定义类HTTest的alloc方法进入callAlloc被调用2次
在探究之前,我们先做个小测试。
我们直接加入callAlloc符号断点,直接启动程序


我们发现第一个进入cls的是NSArray,我们加设断点。

发现他没有进入_objc_rootAllocWithZone,而是发送alloc的消息。
为什么?
- 因为NSArray没有
alloc方法。objc_msgSend发送alloc消息的意思是,求助系统:“老大,我要alloc一下”。这时系统会查NSArray有没有alloc方法,没有就往父类NSObject查。 - 由于
NSObject的初始化,系统在llvm编译时就已经初始化好了。所以此时会直接响应NSObject的alloc类方法。我们加入断点继续查看



我们发现,NSArray的alloc最终还是交给了系统allocWithZone去处理。😂
[HTTest alloc]
当我们加入HTTest的断点后,会经历系统的NSArray、 NSThread、NSMutableDictionary、NSSet、NSUUID的初始化,最终进入HTTest的alloc方法。
与NSArray不同的是,HTTest的第二次调用是_objc_rootAllocWithZone

那么,问题还是没有回答,知道第一次是发送了alloc消息给系统。但是为什么会调用第二次?
我们回到llvm源码。
-
上面知道了系统将
alloc消息转发成objc_alloc
image.png 我们搜索
tryGenerateSpecializedMessageSend函数,查看它什么时候被调用的

我们任何runtime消息被处理时,都会先来到这个函数
GeneratePossiblySpecializedMessageSend。看到上面
被调用的函数在这里作为了if判断条件。条件每次都会try先尝试一遍,所以这里会调用objc_alloc。结果为
false(没找到),就会执行普通消息发送GenerateMessageSend,就会进入当前的alloc流程。
实例检验
image.png
断点进入的顺序如下:image.png可以发现,任何类
调用alloc时,并不会先进入Alloc类方法直接执行
系统使用
objc_msgSend消息机制发送了alloc消息消息会先到达
llvm层的GeneratePossiblySpecializedMessageSend函数,条件判断中触发tryGenerateSpecializedMessageSend函数
tryGenerateSpecializedMessageSend函数内部将alloc消息转发成了objc_alloc消息。所以我们第一次响应是objc_alloc->callAlloc第一次时,类没有实现,所以
tryGenerateSpecializedMessageSend返回false,if条件不成立,发送GenerateMessageSend常规方法,调用alloc类方法创建。此时会调用常规的
alloc->_objc_rootAlloc->callAlloc流程。
拓展:
- 当一个
类创建完第一个实例,再次创建第二个实例时,tryGenerateSpecializedMessageSend会返回true。我们只会响应第一次。不会调用常规的alloc方法哦。 这是缓存的作用。后面熟悉cache和objc_msgSend机制后,相信你就懂了。
如:HTTest * test1 = [HTTest alloc]; HTTest * test2 = [HTTest alloc];
总结
-
NSObject的创建由系统处理 - 自定义类
alloc创建时,会执行两次,第一次判断条件时执行,第二次常规方法执行。
下一节,OC底层原理六: 内存对齐


