iOS底层系列05 --自定义对象alloc方法的深层次探索
2021-02-05 本文已影响0人
YanZi_33
- 先前在alloc init new方法的探索这篇文章中详细阐述了NSObject的alloc方法底层的调用流程,但是不够完善,本篇来探讨自定义对象 alloc方法的底层执行逻辑,首先来回顾一下NSObject的alloc方法;
自定义类YYPerson对象的alloc探索
- 将工程中NSObject改成YYPerson,如下所示:

-
先将源码中断点置为
Disable
,重新运行工程,当断点停在main函数的YYPerson *p1 = [YYPerson alloc]
时,再将源码中断点打开,这么做是为了保证执行到objc_alloc
函数时传进来的参数是YYPerson
; -
接下来的执行流程:
callAlloc
->objc_msgSend()
->alloc
->_objc_rootAlloc
->callAlloc
->_objc_rootAllocWithZone
->_class_createInstanceFromZone
-> 完成YYPerson对象的alloc流程; -
可以看出YYPerson的alloc与NSObject的alloc,底层执行流程不太一样,在YYPerson的alloc中 函数
callAlloc
执行了两次,而在NSObject的alloc中函数callAlloc
执行了一次,这是最大的区别; -
在YYPerson的alloc中,两次执行
callAlloc
函数时,其内部逻辑为如下图所示:

- 看到这里提出三个问题?
- 问题一:无论是NSObject系统对象还是自定义YYPerson对象,alloc时底层为什么会首先调用
objc_alloc
函数而不是调用alloc
的函数实现? - 问题二:NSObject系统对象和自定义YYPerson对象的alloc底层调用逻辑为什么会存在差异?
- 问题三:自定义YYPerson对象在第一次alloc时,callAlloc函数会调用两次,为什么在第二次alloc时,callAlloc函数只调用一次?
- 问题一:无论是NSObject系统对象还是自定义YYPerson对象,alloc时底层为什么会首先调用
针对问题一:alloc函数,底层并没有去调用自己的实现而是去调用了objc_alloc
函数,猜想很有可能是系统利用Runtime做了method swizzle(方法交换)
,这里需要借助LLVM源码
进行进一步的分析,LLVM的源码下载很耗时,我借助了别人的截图;
- 打开llvm源码文件,搜索
alloc
,找到CGObjC.cpp
文件

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

- 在这里,我们看到发送了一个
objc_alloc
消息; - 到这里,我们已经知道objc4源码中objc_alloc信号是从这发出的;
- 具体的发送机制,后续会做详细的阐述,现在我们只要知道
NSObject的alloc和自定义YYPerson的alloc是系统在LLVM底层做了处理帮我们转发到objc_alloc函数
,LLVM在我们编译启动时,就已经处理好了;
针对问题二:现在我们知道对象alloc底层调用:
- 首先会被LLVM处理转发到
objc_alloc
函数去执行,所以第一次调用callAlloc函数
即objc_alloc -> callAlloc
, - 然后判断
!cls->ISA()->hasCustomAWZ():即当前类的cache_t中是否存在alloc/allocWithZone方法
,没有就会进入objc_msgSend(cls, @selector(alloc)
,第二次调用callAlloc函数
即alloc -> _objc_rootAlloc -> callAlloc
,并将alloc方法缓存到cache_t中
; - 系统类NSObject,cache_t缓存中默认是存在alloc/allocWithZone方法;
- 自定义类YYPerson,cache_t缓存中默认没有alloc/allocWithZone方法;
针对问题三
- 当YYPerson类在第一次alloc时,会两次调用
callAlloc
函数; - 当YYPerson类在第二次alloc时,首先消息转发成
objc_alloc
消息,所以第一次调用callAlloc函数
即objc_alloc -> callAlloc
,由于第一次alloc调用之后 cache_t会进行缓存,再次判断!cls->ISA()->hasCustomAWZ():即当前类的cache_t中是否存在alloc/allocWithZone方法
,存在,就不会再执行objc_msgSend(cls, @selector(alloc)
,所以callAlloc函数
只会调用一次;