inout 从 汇编 验证Swift的 inout 本质( 二 )

inout 从 汇编 验证Swift的 inout 本质


文章图片

用于分析关键点的汇编代码
初始化
1 0x 100001 a04:callq 0x 100001 d50;inout . person . _ _ allocation _ init-> inout。主要负责人:斯威夫特:16岁
2 0x100001a09 : leaq 0x1798,% rcxinout.p : inout。人
3 0x100001a10 : xorl %r8d,%r8d
4 0x100001a13 : movl %r8d,%edx
->5 0x100001a16 : movq %rax,0x178binout.p : inout。人
第1行:
_ _分配_初始化
众所周知,类的内存存储在堆空之间,而_ _ allocating _ init就是申请堆空之间的内存
这里我们看一下类的内存分布
第5行:内存应用完成后,存储返回值为rax%,返回Person应用的堆空之间的内存地址
通过断点行5,寄存器读取rax获得一个地址值
rax = 0x00000001006318c0
打开调试->调试工作流->视图内存并输入此地址
下图

inout 从 汇编 验证Swift的 inout 本质


文章图片

结论是->第16个字节确实存储0x12,即p.age的值是18。
传递参数
....
->1.0x100001a77 : movq %rdx,%rdi
2.0x100001a7a : movq %rax,-0x80
3.0x 100001 a7e:callq 0x 100001 af 0;inout.change -> at main.swift:12
根据案例1的分析,rdi%作为参数,这里打印的地址值为0x1006318D0
你发现了吗?
0x1006318D0比0x00000001006318c0多16个字节
这是什么意思?
函数输入参数的地址是个人地址偏移16字节,这是年龄的内存地址
摘要
inout函数还通过更改类对象类的存储属性来更改内存地址中的值
这也是一个参考转移
具体流程如下

inout 从 汇编 验证Swift的 inout 本质


文章图片

类的计算属性传递
添加计算的属性计数
功能更改{
数量= 20
}
人员类别{
var年龄= 18岁
var计数:Int {
设置{
年龄=新值* 2
}
获取{
返回年龄/ 2
}
}
}
风险值=人
变化
首先,我们尝试打印p的内存足迹。
MemoryLayout.size
结果仍然是8字节,这意味着
计算属性不占用类的内存大小。它相当于一个方法调用,存储在当前函数的堆栈空中
试着猜猜?
如果计算出的属性不占用P的内存空,则意味着无法从P获取count的内存地址。
调用inout函数不能改变count属性,因为没有地址输入
这符合上述验证
所以结果是
打印
// 20
计数已更改

inout 从 汇编 验证Swift的 inout 本质


文章图片

看看汇编
1.0x 1000015 D4:callq 0x 100001 bb 0;inout . person . _ _ allocation _ init-> inout。主要负责人:斯威夫特:16岁
...
..
-> 2.0x100001648 : callq *%rdx
3.0x10000164a : movq %rdx,%rdi
4.0x10000164d : movq %rax,-0x80
5.0x 100001651:callq 0x 1000016 c0;inout.change -> at main.swift:12
6.0x100001658 : movq -0x78,%rdi
7.0x100001660 : callq *%rax
同样,在初始化Person之后,我们可以看到第3行中rdi%的值是从rdx%获得的
第2行
callq *%rdx
这是一个间接调用指令,rdx%存储一个用于跳转的间接地址
为什么这是间接地址?
由于类的继承性,属性很可能被重写,系统不确定这个计算属性的setter getter是否被重写
您只能在运行时找到相应的方法地址
这是间接寻址
好的,继续输入si,然后进去
inout`Person.count.modify:

推荐阅读