iOS底层系列05 --自定义对象alloc方法的深层次探索

  • 先前在alloc init new方法的探索这篇文章中详细阐述了NSObject的alloc方法底层的调用流程,但是不够完善,本篇来探讨自定义对象 alloc方法的底层执行逻辑,首先来回顾一下NSObject的alloc方法;

自定义类YYPerson对象的alloc探索

  • 将工程中NSObject改成YYPerson,如下所示:
image.png
  • 先将源码中断点置为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函数时,其内部逻辑为如下图所示:

YYPerson_alloc.png
  • 看到这里提出三个问题?
    • 问题一:无论是NSObject系统对象还是自定义YYPerson对象,alloc时底层为什么会首先调用objc_alloc函数而不是调用alloc的函数实现?
    • 问题二:NSObject系统对象和自定义YYPerson对象的alloc底层调用逻辑为什么会存在差异?
    • 问题三:自定义YYPerson对象在第一次alloc时,callAlloc函数会调用两次,为什么在第二次alloc时,callAlloc函数只调用一次?
针对问题一:alloc函数,底层并没有去调用自己的实现而是去调用了objc_alloc函数,猜想很有可能是系统利用Runtime做了method swizzle(方法交换),这里需要借助LLVM源码进行进一步的分析,LLVM的源码下载很耗时,我借助了别人的截图;
  • 打开llvm源码文件,搜索alloc,找到CGObjC.cpp文件
Snip20210204_56.png
  • 可以看到这里有明确标注,[self alloc] -> objc_alloc(self)
  • 函数中显示,当接收到alloc名称的selector时,调用EmitObjCAlloc函数,我们继续搜索EmitObjCAlloc;
Snip20210204_57.png
  • 在这里,我们看到发送了一个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函数只会调用一次;

你可能感兴趣的:(iOS底层系列05 --自定义对象alloc方法的深层次探索)