在上篇文章中我們分析了閉包中捕獲了一個外部變量時其底層的參數(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處斷點,然后進(jìn)入到匯編代碼。

圖1
我們知道在
return之前,會將外部變量捕獲并拷貝到堆空間,那么必然會alloc分配空間,此時我們直接找關(guān)鍵信息即可。但是這時我們發(fā)現(xiàn)有3個alloc,我們先不管,看第19行,可以看到將1放入到了0x10(%rax)中,而rax中存放的是我們分配在堆空間的地址,此時往后移16個字節(jié)。也就是直接寫入到第3位8字節(jié)。
同理我們直接看第2個 alloc,可以得其將b放入到空間。

圖2
這里我們看第三個
alloc(后文稱之為變量x),同樣分配的堆空間的地址是通過rax返回的,此時會先將-0x30(%rbp)的數(shù)據(jù)放入到rcx中,而-0x30(%rbp)可以參考圖1的第23行處,也就是給a分配堆空間后將其也放入到了-0x30(%rbp)中,而圖2中可以知道rcx中的地址就是變量a的地址,而在41行處將該地址給了0x10(%rax),可以知道這里是直接將變量a賦值給了x的第3個8字節(jié),也就是變量x持有了變量a,同樣第42行和43行可知將變量b賦值給了變臉x的第4個8字節(jié)。

圖3
看第45行處可以知道,此時將變量c放在了
-0x50(%rbp)中,而在第50行處又放在了寄存器rdx中,從之前的文章中我們也知道寄存器rdx可以存放函數(shù)參數(shù)。
此時回到調(diào)用getFn處,這里的傳參邏輯與捕獲一個外部變量一致,
可參考前文Swift匯編分析閉包-調(diào)用原理