本文首發於個人我的博客html
先回顧一下,上一篇 Swift之閉包(Closure)中對閉包的解釋git
先看下面一段代碼,猜猜會輸出什麼github
typealias Fn = (Int) -> Int
func getFn() -> Fn{
// 局部變量
var num = 0
func plus(_ i: Int) -> Int{
num += i
return num
}
return plus(_:)
}
var fn = getFn()
print(fn(1)) // 1
print(fn(2)) // 3
print(fn(3)) // 6
print(fn(4)) // 10
複製代碼
結果是輸出編程
1
3
6
10
複製代碼
那麼,問題來了,爲何輸出的是10呢?由於按照常識,var num = 0 是局部變量,執行完就銷燬了,怎麼能再後面繼續使用呢?swift
咱們先從簡單的提及 首先是下面一端代碼bash
typealias Fn = (Int) -> Int
func getFn() -> Fn{
// 局部變量
var num = 0
func plus(_ i: Int) -> Int{
return i
}
return plus(_:) // 這裏打斷點
}
var fn = getFn()
print(fn(1))
複製代碼
先不適用num ,直接 return i 並在這裏打斷點,結果以下閉包
testSwift`getFn():
0x100001f70 <+0>: pushq %rbp
0x100001f71 <+1>: movq %rsp, %rbp
0x100001f74 <+4>: movq $0x0, -0x8(%rbp)
-> 0x100001f7c <+12>: leaq 0xd(%rip), %rax ; plus #1 (Swift.Int) -> Swift.Int in testSwift.getFn() -> (Swift.Int) -> Swift.Int at main.swift:23
0x100001f83 <+19>: xorl %ecx, %ecx
0x100001f85 <+21>: movl %ecx, %edx
0x100001f87 <+23>: popq %rbp
0x100001f88 <+24>: retq
複製代碼
可知,0xd(%rip), %rax 這段代碼,把地址值,也就是getFn() 函數的地址值給了rax, 根本沒有alloc malloc等代碼,也就是說,沒有開闢堆空間。那麼接下來咱們看下面的代碼app
typealias Fn = (Int) -> Int
func getFn() -> Fn{
// 局部變量
var num = 0
func plus(_ i: Int) -> Int{
num += i
return num
}
return plus(_:) // 這裏打斷點
}
var fn = getFn()
print(fn(1))
print(fn(2))
print(fn(3))
複製代碼
斷點以下函數
testSwift`getFn():
0x100001de0 <+0>: pushq %rbp
0x100001de1 <+1>: movq %rsp, %rbp
0x100001de4 <+4>: subq $0x20, %rsp
0x100001de8 <+8>: leaq 0x3301(%rip), %rdi
0x100001def <+15>: movl $0x18, %esi
0x100001df4 <+20>: movl $0x7, %edx
// 這裏swift_allocObject 說明產生了堆空間
0x100001df9 <+25>: callq 0x1000046f8 ; symbol stub for: swift_allocObject
0x100001dfe <+30>: movq %rax, %rdx
0x100001e01 <+33>: addq $0x10, %rdx
0x100001e05 <+37>: movq %rdx, %rsi
0x100001e08 <+40>: movq $0x0, 0x10(%rax)
-> 0x100001e10 <+48>: movq %rax, %rdi
0x100001e13 <+51>: movq %rax, -0x8(%rbp)
0x100001e17 <+55>: movq %rdx, -0x10(%rbp)
0x100001e1b <+59>: callq 0x100004758 ; symbol stub for: swift_retain
0x100001e20 <+64>: movq -0x8(%rbp), %rdi
0x100001e24 <+68>: movq %rax, -0x18(%rbp)
0x100001e28 <+72>: callq 0x100004752 ; symbol stub for: swift_release
0x100001e2d <+77>: movq -0x10(%rbp), %rax
0x100001e31 <+81>: leaq 0x178(%rip), %rax ; partial apply forwarder for plus #1 (Swift.Int) -> Swift.Int in testSwift.getFn() -> (Swift.Int) -> Swift.Int at <compiler-generated>
0x100001e38 <+88>: movq -0x8(%rbp), %rdx
0x100001e3c <+92>: addq $0x20, %rsp
0x100001e40 <+96>: popq %rbp
0x100001e41 <+97>: retq
複製代碼
進一步驗證,下面的代碼是由於,寫文章的時候,從新跑了一遍,因此函數 getFn() 函數的抵制和截圖不一致,是post
rax = 0x0000000101849fd0
此次咱們在
typealias Fn = (Int) -> Int
func getFn() -> Fn{
// 局部變量
var num = 0
func plus(_ i: Int) -> Int{
num += i
return num // 第二次這裏打斷點 查看getFn()地址的內容
}
return plus(_:) // 第一次這裏打斷點 獲取getFn()地址
}
var fn = getFn()
print(fn(1))
print(fn(2))
print(fn(3))
複製代碼
由於調用了三次 fn分別爲 fn(1) 、 fn(2)、fn(3),因此在 return num 地方,會斷三次 咱們分別查看函數getFn() 函數地址的內容
結果如圖
圖中可知,確實是操做同一塊堆空間,並且以前Swift之類中講過,前面16個字節,分別存放 類的信息,引用技術,而後後面纔是值,可知,
剛開始分配完,堆空間裏面是垃圾數據 執行完 print(fn(1)) 以後,堆空間裏面放的是1 執行完 print(fn(2)) 以後,堆空間裏面放的是3 執行完 print(fn(3)) 以後,堆空間裏面放的是6
這也解釋了,文章開頭的那個疑問,由於閉包捕獲了局部變量,在堆中開闢空間,而後後面調用的時候,操做的是堆空間的內存,因此結果是
1
3
6
10
複製代碼
關於彙編的調試指令能夠參考