美文网首页iOS 底层原理 iOS 进阶之路
OC底层原理五: NSObject的alloc分析

OC底层原理五: NSObject的alloc分析

作者: markhetao | 来源:发表于2020-09-12 01:09 被阅读0次

OC底层原理 学习大纲

在编译时,发现两个问题:
问题1. NSObjectalloc方法不走常规路
问题2. 自定义类HTTestalloc方法进入callAlloc被调用2次

发现路径:

问题1:NSObjectalloc方法不走常规路

我们在[NSObject alloc]入口和alloc类方法打断点:

图一.png image.png

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

汇编模式.png

发现下一个执行函数是objc_alloc

image.png

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

image.png

发现此时cls刚好是NSObject

NSObject alloc.png

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

image.png

新问题:NSObject是如何忽略alloc,直接调用objc_alloc函数的呢?

这里需要通过llvm源码来探究。 源码1G多,这里我展示一下就行。实际上NSObject是系统自动创建和管理的。

  • VSCode中打开llvm源码文件,搜索alloc,找到CGObjC.cpp文件夹。
image.png
  • 可以看到这里有明确标注,[self alloc] -> objc_alloc(self)
  • 函数中显示,当接收到alloc名称的selector时,调用EmitObjCAlloc函数。我们继续搜索EmitObjCAlloc
image.png
  • 在这里,我们看到发射了一个objc_alloc消息
  • 到这里,我们已经知道objc4源码中objc_alloc信号是从这发出的。
  • 具体的发送机制,后续课程会深入了解。 现在我们只要知道,NSObjectalloc是系统在llvm底层帮我们转发到objc_allocLLVM在我们编译启动时,就已经处理好了 👍

问题2:自定义类HTTestalloc方法进入callAlloc被调用2次

在探究之前,我们先做个小测试。
我们直接加入callAlloc符号断点,直接启动程序

callAlloc断点 NSArray

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

image.png

发现他没有进入_objc_rootAllocWithZone,而是发送alloc的消息。

为什么?

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

我们发现,NSArrayalloc最终还是交给了系统allocWithZone去处理。😂

[HTTest alloc]

当我们加入HTTest的断点后,会经历系统的NSArray、 NSThread、NSMutableDictionary、NSSet、NSUUID的初始化,最终进入HTTestalloc方法。
与NSArray不同的是,HTTest的第二次调用是_objc_rootAllocWithZone

image.png

那么,问题还是没有回答,知道第一次是发送了alloc消息给系统。但是为什么会调用第二次?

我们回到llvm源码。

  • 上面知道了系统将alloc消息转发成objc_alloc

    image.png
  • 我们搜索tryGenerateSpecializedMessageSend函数,查看它什么时候被调用的

image.png
  • 我们任何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返回falseif条件不成立,发送GenerateMessageSend常规方法,调用alloc类方法创建。

  • 此时会调用常规的alloc->_objc_rootAlloc->callAlloc流程。

拓展:

  • 当一个创建完第一个实例,再次创建第二个实例时,tryGenerateSpecializedMessageSend会返回true。我们只会响应第一次。不会调用常规的alloc方法哦。 这是缓存的作用。后面熟悉cacheobjc_msgSend机制后,相信你就懂了。
    如:
HTTest * test1  = [HTTest alloc];
HTTest * test2  = [HTTest alloc];

总结

  • NSObject的创建由系统处理
  • 自定义类alloc创建时,会执行两次,第一次判断条件时执行,第二次常规方法执行。

下一节,OC底层原理六: 内存对齐

相关文章

网友评论

    本文标题:OC底层原理五: NSObject的alloc分析

    本文链接:https://www.haomeiwen.com/subject/nhuiektx.html