前言
本文将会回答两个问题:
-
什么场景会调用
objc_copyCppObjectAtomic
函数? 在上篇文章中,我们并没有发现任何场景会调用objc_copyCppObjectAtomic
函数。 -
copyHelper 在哪里? iOS 中 copy 的原理中曾经提到作者没有找到 copyHelper(dest, src); 的实现方法。
含 c++ 类的复制行为
为了解释前言中的两个问题,我们需要在 CopyMock
新增了一个属性 str
,该属性的类型是 std::string
。
|
|
与第一篇文章类似,我们先将代码编译为中间码。如下所示:
|
|
编译器会先帮开发者增加一个普通的赋值方法 -[CopyMock setStr:]
。
该方法最后会调用 objc_copyCppObjectAtomic
函数,
三个参数分别是:
- 属性str的存储地址,CopyMock 实例的偏移 32 位
%8 = getelementptr inbounds i8, i8* %7, i64 32, !dbg !1904
- 被复制
str
的地址%11 = bitcast %"class.std::__1::basic_string"* %2 to i8*, !dbg !1904
- 由编译器生成的辅助复制函数
__assign_helper_atomic_property_
__assign_helper_atomic_property_
在分析 __assign_helper_atomic_property_
的内部逻辑前,我们需要再看一遍 objc_copyCppObjectAtomic
函数的声明: void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source))
。
通过原型我们可以发现 copyHelper
是 objc_copyCppObjectAtomic
的第三个参数。
实际上,void (*copyHelper) (void *dest, const void *source))
是一个函数指针,它的实现是通过调用方传参决定。在本例中,它会指向 __assign_helper_atomic_property_
。
在阅读 __assign_helper_atomic_property_
的代码前,需要先准备几个小知识:
- c++ 的函数名会被 name mangle,所以
_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_
实际上代表字符串复制函数std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::operator=(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
- 中间码具有 ssp 特征,阅读比较繁琐,编译后的汇编会被高度优化,下一章会讲到编译器的优化
- c++ 中的
std::string
和class.std::__1::basic_string
是等价的 - alloca 可以在栈中开辟空间,具有效率高,不需要主动释放等特性
小知识准备结束,下面开始对 __assign_helper_atomic_property_
的实现内容进行分析:
|
|
这个函数的逻辑很简单,相当于对 字符串复制函数 std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::operator=(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
进行了一次间接调用。
总结
本文通过将代码改造为 cpp 代码,可以得到以下两个信息:
- copyHelper 通常是由编译器生成的辅助函数
- 具有 cpp 相关类复制的场景才会触发对
objc_copyCppObjectAtomic
函数的调用