本文将会回答两个问题:
objc_copyCppObjectAtomic
函数?
在上篇文章中,我们并没有发现任何场景会调用 objc_copyCppObjectAtomic
函数。为了解释前言中的两个问题,我们需要在 CopyMock
新增了一个属性 str
,该属性的类型是 std::string
。
#include <string>
@interface CopyMock : NSObject
@property (readwrite) std::string str;
@end
与第一篇文章类似,我们先将代码编译为中间码。如下所示:
// 赋值函数
; Function Attrs: noinline optnone ssp uwtable
define internal void @"\01-[CopyMock setStr:]"(%0*, i8*, %"class.std::__1::basic_string"*) #0 !dbg !1897 {
%4 = alloca %0*, align 8
%5 = alloca i8*, align 8
store %0* %0, %0** %4, align 8
call void @llvm.dbg.declare(metadata %0** %4, metadata !1900, metadata !DIExpression()), !dbg !1901
store i8* %1, i8** %5, align 8
call void @llvm.dbg.declare(metadata i8** %5, metadata !1902, metadata !DIExpression()), !dbg !1901
call void @llvm.dbg.declare(metadata %"class.std::__1::basic_string"* %2, metadata !1903, metadata !DIExpression()), !dbg !1901
%6 = load %0*, %0** %4, align 8, !dbg !1904
%7 = bitcast %0* %6 to i8*, !dbg !1904
%8 = getelementptr inbounds i8, i8* %7, i64 32, !dbg !1904
%9 = bitcast i8* %8 to %"class.std::__1::basic_string"*, !dbg !1904
%10 = bitcast %"class.std::__1::basic_string"* %9 to i8*, !dbg !1904
%11 = bitcast %"class.std::__1::basic_string"* %2 to i8*, !dbg !1904
call void @objc_copyCppObjectAtomic(i8* %10, i8* %11, i8* bitcast (void (%"class.std::__1::basic_string"*, %"class.std::__1::basic_string"*)* @__assign_helper_atomic_property_ to i8*)), !dbg !1904
ret void, !dbg !1904
}
编译器会先帮开发者增加一个普通的赋值方法 -[CopyMock setStr:]
。
该方法最后会调用 objc_copyCppObjectAtomic
函数,
三个参数分别是:
`%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_
的代码前,需要先准备几个小知识:
_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&)
std::string
和 class.std::__1::basic_string
是等价的小知识准备结束,下面开始对 __assign_helper_atomic_property_
的实现内容进行分析:
// 辅助赋值函数
; Function Attrs: noinline ssp uwtable
define internal void @__assign_helper_atomic_property_(%"class.std::__1::basic_string"*, %"class.std::__1::basic_string"*) #5 !dbg !1891 {
// 在栈中开辟空间
%3 = alloca %"class.std::__1::basic_string"*, align 8
// 在栈中开辟空间,备用
%4 = alloca %"class.std::__1::basic_string"*, align 8
// 存储参数 %0 到 %3
store %"class.std::__1::basic_string"* %0, %"class.std::__1::basic_string"** %3, align 8
call void @llvm.dbg.declare(metadata %"class.std::__1::basic_string"** %3, metadata !1892, metadata !DIExpression()), !dbg !1893
// 存储参数 %1 到 %4
store %"class.std::__1::basic_string"* %1, %"class.std::__1::basic_string"** %4, align 8
call void @llvm.dbg.declare(metadata %"class.std::__1::basic_string"** %4, metadata !1894, metadata !DIExpression()), !dbg !1893
// 取出参数 %4 到 %5
%5 = load %"class.std::__1::basic_string"*, %"class.std::__1::basic_string"** %4, align 8, !dbg !1893
// 取出参数 %3 到 %6
%6 = load %"class.std::__1::basic_string"*, %"class.std::__1::basic_string"** %3, align 8, !dbg !1893
// 调用字符串复制方法 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&)
%7 = call dereferenceable(24) %"class.std::__1::basic_string"* @_ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEaSERKS5_(%"class.std::__1::basic_string"* %6, %"class.std::__1::basic_string"* dereferenceable(24) %5), !dbg !1893
ret void, !dbg !1895
}
这个函数的逻辑很简单,相当于对 字符串复制函数 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 代码,可以得到以下两个信息:
objc_copyCppObjectAtomic
函数的调用