在上篇文章中我們分析了閉包中捕獲了一個(gè)外部變量時(shí)其底層的參數(shù)傳遞邏輯熄守,那么如果捕獲兩個(gè)外部變量時(shí)呢,其又是怎么傳參的熙卡。
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(函數(shù)地址)
//16 = 8 + 8
var fn = getFn()
fn(10)
同樣我們在 return plus
處斷點(diǎn)洲脂,然后進(jìn)入到匯編代碼斤儿。
圖1
我們知道在
return
之前,會(huì)將外部變量捕獲并拷貝到堆空間恐锦,那么必然會(huì)alloc
分配空間往果,此時(shí)我們直接找關(guān)鍵信息即可。但是這時(shí)我們發(fā)現(xiàn)有3個(gè)alloc
,我們先不管一铅,看第19行陕贮,可以看到將1
放入到了0x10(%rax)
中,而rax
中存放的是我們分配在堆空間的地址潘飘,此時(shí)往后移16
個(gè)字節(jié)肮之。也就是直接寫入到第3位8字節(jié)。
同理我們直接看第2個(gè) alloc
,可以得其將b放入到空間卜录。
圖2
這里我們看第三個(gè)
alloc
(后文稱之為變量x),同樣分配的堆空間的地址是通過rax
返回的戈擒,此時(shí)會(huì)先將-0x30(%rbp)
的數(shù)據(jù)放入到rcx
中,而-0x30(%rbp)
可以參考圖1的第23行處艰毒,也就是給a
分配堆空間后將其也放入到了-0x30(%rbp)
中筐高,而圖2中可以知道rcx
中的地址就是變量a
的地址,而在41行處將該地址給了0x10(%rax)
,可以知道這里是直接將變量a
賦值給了x的第3個(gè)8字節(jié),也就是變量x持有了變量a柑土,同樣第42行和43行可知將變量b賦值給了變臉x的第4個(gè)8字節(jié)蜀肘。
圖3
看第45行處可以知道,此時(shí)將變量c放在了
-0x50(%rbp)
中稽屏,而在第50行處又放在了寄存器rdx
中幌缝,從之前的文章中我們也知道寄存器rdx
可以存放函數(shù)參數(shù)。
此時(shí)回到調(diào)用getFn處诫欠,這里的傳參邏輯與捕獲一個(gè)外部變量一致,
可參考前文Swift匯編分析閉包-調(diào)用原理