在上篇文章中我们分析了闭包中捕获了一个外部变量时其底层的参数传递逻辑,那么如果捕获两个外部变量时呢,其又是怎么传参的。
typealias Fn = (Int) -> (Int, Int)
func getFn() -> Fn {
var a = 1
var b = 2
func plus(_ i: Int) -> (Int, Int) {
a += i
b += i * 2
return (a, b)
}
return plus
}
//rax(函数地址)
//16 = 8 + 8
var fn = getFn()
fn(10)
同样我们在 return plus
处断点,然后进入到汇编代码。
我们知道在
return
之前,会将外部变量捕获并拷贝到堆空间,那么必然会alloc
分配空间,此时我们直接找关键信息即可。但是这时我们发现有3个alloc
,我们先不管,看第19行,可以看到将1
放入到了0x10(%rax)
中,而rax
中存放的是我们分配在堆空间的地址,此时往后移16
个字节。也就是直接写入到第3位8字节。
同理我们直接看第2个 alloc
,可以得其将b放入到空间。
这里我们看第三个
alloc
(后文称之为变量x),同样分配的堆空间的地址是通过rax
返回的,此时会先将-0x30(%rbp)
的数据放入到rcx
中,而-0x30(%rbp)
可以参考图1的第23行处,也就是给a
分配堆空间后将其也放入到了-0x30(%rbp)
中,而图2中可以知道rcx
中的地址就是变量a
的地址,而在41行处将该地址给了0x10(%rax)
,可以知道这里是直接将变量a
赋值给了x的第3个8字节,也就是变量x持有了变量a,同样第42行和43行可知将变量b赋值给了变脸x的第4个8字节。
图3
看第45行处可以知道,此时将变量c放在了
-0x50(%rbp)
中,而在第50行处又放在了寄存器rdx
中,从之前的文章中我们也知道寄存器rdx
可以存放函数参数。
此时回到调用getFn处,这里的传参逻辑与捕获一个外部变量一致,
可参考前文Swift汇编分析闭包-调用原理
网友评论