12
Swift 拾遗 - 闭包
source link: https://kingcos.me/posts/2020/swift_closure/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Preface
《Swift 拾遗》是一个关于 Swift 的新文章专辑,这个系列的文章将不会涉及基本语法的层面,而是尝试从底层「拾」起之前所忽视的内容。今天我们将一起简单探究 Swift 中的闭包(Closure)。
我们将参数和返回值类型为 Int
的函数(也可称作闭包)(Int) -> Int
易名为 Closure
,并定义一个函数返回该闭包类型:
// main.swift
typealias Closure = (Int) -> Int
func foo() -> Closure {
var bar = 5
func baz(_ val: Int) -> Int {
bar += val // BREAKPOINT 🔴
return bar
}
bar = 10
return baz // BREAKPOINT 🔴
}
var f = foo()
f(1) // 11
f(2) // 13
f(3) // 16
通过 f(n)
函数多次执行的结果,我们发现局部变量 bar
似乎在每次调用后并没有被销毁,原因其实是 baz
对 bar
进行了「捕获」,接下来我们将仔细研究这一过程。
demo`foo():
0x100000d70 <+0>: pushq %rbp
0x100000d71 <+1>: movq %rsp, %rbp
0x100000d74 <+4>: subq $0x20, %rsp
0x100000d78 <+8>: leaq 0x299(%rip), %rdi
0x100000d7f <+15>: movl $0x18, %esi
0x100000d84 <+20>: movl $0x7, %edx
; ⚠️:这里调用了 swift_allocObject,即分配了堆空间
0x100000d89 <+25>: callq 0x100000f14 ; symbol stub for: swift_allocObject
; alloc 函数调用后将返回堆空间首地址,并存储在 rax 寄存器
0x100000d8e <+30>: movq %rax, %rcx
0x100000d91 <+33>: addq $0x10, %rcx
0x100000d95 <+37>: movq %rcx, %rdx
; 初始化:将立即数 5 存储到 rax + 0x10 地址,即偏移 16 个字节
0x100000d98 <+40>: movq $0x5, 0x10(%rax)
; 二次赋值:将立即数 10 存储到 rax + 0x10 地址
0x100000da0 <+48>: movq $0xa, 0x10(%rax)
-> 0x100000da8 <+56>: movq %rax, %rdi
; 将 rax 寄存器内容(即封装了 bar 的堆空间地址)移动到 rbp - 0x8
0x100000dab <+59>: movq %rax, -0x8(%rbp)
0x100000daf <+63>: movq %rcx, -0x10(%rbp)
0x100000db3 <+67>: callq 0x100000f32 ; symbol stub for: swift_retain
0x100000db8 <+72>: movq -0x8(%rbp), %rdi
0x100000dbc <+76>: movq %rax, -0x18(%rbp)
0x100000dc0 <+80>: callq 0x100000f2c ; symbol stub for: swift_release
0x100000dc5 <+85>: movq -0x10(%rbp), %rax
; 将 rip + 0x130(0x0000000100000f00)地址内容(即包装 baz 的函数的地址)移动到 rax 寄存器
0x100000dc9 <+89>: leaq 0x130(%rip), %rax ; partial apply forwarder for baz #1 (Swift.Int) -> Swift.Int in demo.foo() -> (Swift.Int) -> Swift.Int at <compiler-generated>
; 将 rbp - 0x8 地址内容(即封装了 bar 的堆空间地址)移动到 rdx 寄存器
0x100000dd0 <+96>: movq -0x8(%rbp), %rdx
0x100000dd4 <+100>: addq $0x20, %rsp
0x100000dd8 <+104>: popq %rbp
0x100000dd9 <+105>: retq
demo`main:
0x100000bd0 <+0>: pushq %rbp
0x100000bd1 <+1>: movq %rsp, %rbp
0x100000bd4 <+4>: pushq %r13
0x100000bd6 <+6>: subq $0xc8, %rsp
0x100000bdd <+13>: movl %edi, -0x54(%rbp)
0x100000be0 <+16>: movq %rsi, -0x60(%rbp)
0x100000be4 <+20>: callq 0x100000d70 ; demo.foo() -> (Swift.Int) -> Swift.Int at main.swift:5
; 函数调用后将把返回值(即 0x0000000100000f00,包装 baz 的函数的地址)存储到 rax 寄存器
0x100000be9 <+25>: leaq 0x1450(%rip), %rcx ; demo.f : (Swift.Int) -> Swift.Int
0x100000bf0 <+32>: xorl %edi, %edi
0x100000bf2 <+34>: movl %edi, %esi
; 将 rax 寄存器内容(即包装 baz 的函数的地址)移动到 rip + 0x1445(0x100002040)
0x100000bf4 <+36>: movq %rax, 0x1445(%rip) ; demo.f : (Swift.Int) -> Swift.Int
; 将 rdx 寄存器内容(即封装了 bar 的堆空间地址)移动到 rip + 0x1446(0x100002048)
0x100000bfb <+43>: movq %rdx, 0x1446(%rip) ; demo.f : (Swift.Int) -> Swift.Int + 8
-> 0x100000c02 <+50>: movq %rcx, %rdi
0x100000c05 <+53>: leaq -0x20(%rbp), %rax
0x100000c09 <+57>: movq %rsi, -0x68(%rbp)
0x100000c0d <+61>: movq %rax, %rsi
0x100000c10 <+64>: movl $0x20, %edx
0x100000c15 <+69>: movq -0x68(%rbp), %rcx
0x100000c19 <+73>: callq 0x100000f1a ; symbol stub for: swift_beginAccess
; 将 rip + 0x141b(0x100002040)地址内容(即包装 baz 的函数的地址)移动到 rax 寄存器
0x100000c1e <+78>: movq 0x141b(%rip), %rax ; demo.f : (Swift.Int) -> Swift.Int
; 将 rip + 0x141c(0x100002048)地址内容(即封装了 bar 的堆空间地址)移动到 rcx 寄存器
0x100000c25 <+85>: movq 0x141c(%rip), %rcx ; demo.f : (Swift.Int) -> Swift.Int + 8
0x100000c2c <+92>: movq %rcx, %rdi
; 将 rax 寄存器内容(即包装 baz 的函数的地址)移动到 rbp - 0x70
0x100000c2f <+95>: movq %rax, -0x70(%rbp)
; 将 rcx 寄存器内容(即封装了 bar 的堆空间地址)移动到 rbp - 0x78
0x100000c33 <+99>: movq %rcx, -0x78(%rbp)
0x100000c37 <+103>: callq 0x100000f32 ; symbol stub for: swift_retain
0x100000c3c <+108>: leaq -0x20(%rbp), %rdi
0x100000c40 <+112>: movq %rax, -0x80(%rbp)
0x100000c44 <+116>: callq 0x100000f26 ; symbol stub for: swift_endAccess
; 将立即数 1(f(1) 中的参数)移动到 edi 寄存器
0x100000c49 <+121>: movl $0x1, %edi
; 将 rbp - 0x78 地址内容(即封装了 bar 的堆空间地址)移动到 r13 寄存器
0x100000c4e <+126>: movq -0x78(%rbp), %r13
; 将 rbp - 0x70 地址内容(即包装 baz 的函数的地址)移动到 rax 寄存器
0x100000c52 <+130>: movq -0x70(%rbp), %rax
; 函数调用,使用 si 进入
0x100000c56 <+134>: callq *%rax
0x100000c58 <+136>: movq -0x78(%rbp), %rdi
0x100000c5c <+140>: movq %rax, -0x88(%rbp)
0x100000c63 <+147>: callq 0x100000f2c ; symbol stub for: swift_release
0x100000c68 <+152>: leaq 0x13d1(%rip), %rax ; demo.f : (Swift.Int) -> Swift.Int
0x100000c6f <+159>: xorl %r8d, %r8d
0x100000c72 <+162>: movl %r8d, %ecx
0x100000c75 <+165>: movq %rax, %rdi
0x100000c78 <+168>: leaq -0x38(%rbp), %rsi
0x100000c7c <+172>: movl $0x20, %edx
0x100000c81 <+177>: callq 0x100000f1a ; symbol stub for: swift_beginAccess
0x100000c86 <+182>: movq 0x13b3(%rip), %rax ; demo.f : (Swift.Int) -> Swift.Int
0x100000c8d <+189>: movq 0x13b4(%rip), %rcx ; demo.f : (Swift.Int) -> Swift.Int + 8
0x100000c94 <+196>: movq %rcx, %rdi
0x100000c97 <+199>: movq %rax, -0x90(%rbp)
0x100000c9e <+206>: movq %rcx, -0x98(%rbp)
0x100000ca5 <+213>: callq 0x100000f32 ; symbol stub for: swift_retain
0x100000caa <+218>: leaq -0x38(%rbp), %rdi
0x100000cae <+222>: movq %rax, -0xa0(%rbp)
0x100000cb5 <+229>: callq 0x100000f26 ; symbol stub for: swift_endAccess
0x100000cba <+234>: movl $0x2, %edi
0x100000cbf <+239>: movq -0x98(%rbp), %r13
0x100000cc6 <+246>: movq -0x90(%rbp), %rax
0x100000ccd <+253>: callq *%rax
0x100000ccf <+255>: movq -0x98(%rbp), %rdi
0x100000cd6 <+262>: movq %rax, -0xa8(%rbp)
0x100000cdd <+269>: callq 0x100000f2c ; symbol stub for: swift_release
0x100000ce2 <+274>: leaq 0x1357(%rip), %rax ; demo.f : (Swift.Int) -> Swift.Int
0x100000ce9 <+281>: xorl %r8d, %r8d
0x100000cec <+284>: movl %r8d, %ecx
0x100000cef <+287>: movq %rax, %rdi
0x100000cf2 <+290>: leaq -0x50(%rbp), %rsi
0x100000cf6 <+294>: movl $0x20, %edx
0x100000cfb <+299>: callq 0x100000f1a ; symbol stub for: swift_beginAccess
0x100000d00 <+304>: movq 0x1339(%rip), %rax ; demo.f : (Swift.Int) -> Swift.Int
0x100000d07 <+311>: movq 0x133a(%rip), %rcx ; demo.f : (Swift.Int) -> Swift.Int + 8
0x100000d0e <+318>: movq %rcx, %rdi
0x100000d11 <+321>: movq %rax, -0xb0(%rbp)
0x100000d18 <+328>: movq %rcx, -0xb8(%rbp)
0x100000d1f <+335>: callq 0x100000f32 ; symbol stub for: swift_retain
0x100000d24 <+340>: leaq -0x50(%rbp), %rdi
0x100000d28 <+344>: movq %rax, -0xc0(%rbp)
0x100000d2f <+351>: callq 0x100000f26 ; symbol stub for: swift_endAccess
0x100000d34 <+356>: movl $0x3, %edi
0x100000d39 <+361>: movq -0xb8(%rbp), %r13
0x100000d40 <+368>: movq -0xb0(%rbp), %rax
0x100000d47 <+375>: callq *%rax
0x100000d49 <+377>: movq -0xb8(%rbp), %rdi
0x100000d50 <+384>: movq %rax, -0xc8(%rbp)
0x100000d57 <+391>: callq 0x100000f2c ; symbol stub for: swift_release
0x100000d5c <+396>: xorl %eax, %eax
0x100000d5e <+398>: addq $0xc8, %rsp
0x100000d65 <+405>: popq %r13
0x100000d67 <+407>: popq %rbp
0x100000d68 <+408>: retq
demo`partial apply for baz #1 (_:) in foo():
-> 0x100000f00 <+0>: pushq %rbp
0x100000f01 <+1>: movq %rsp, %rbp
; rdi 未改变,即第一个参数(1)
; 将 r13 寄存器内容(即封装了 bar 的堆空间地址)移动到 rsi 寄存器(即第二个参数)
0x100000f04 <+4>: movq %r13, %rsi
0x100000f07 <+7>: popq %rbp
; 继续 si
0x100000f08 <+8>: jmp 0x100000e00 ; baz #1 (Swift.Int) -> Swift.Int in demo.foo() -> (Swift.Int) -> Swift.Int at main.swift:8
demo`baz #1 (_:) in foo():
-> 0x100000e00 <+0>: pushq %rbp
0x100000e01 <+1>: movq %rsp, %rbp
0x100000e04 <+4>: subq $0x90, %rsp
0x100000e0b <+11>: xorl %eax, %eax
0x100000e0d <+13>: movl %eax, %ecx
0x100000e0f <+15>: xorl %eax, %eax
0x100000e11 <+17>: leaq -0x8(%rbp), %rdx
; 将 rdi 寄存器内容(即 1)移动到 rbp - 0x48 地址
0x100000e15 <+21>: movq %rdi, -0x48(%rbp)
0x100000e19 <+25>: movq %rdx, %rdi
; 将 rsi 寄存器内容(即封装了 bar 的堆空间地址)移动到 rbp - 0x50 地址
0x100000e1c <+28>: movq %rsi, -0x50(%rbp)
0x100000e20 <+32>: movl %eax, %esi
0x100000e22 <+34>: movl $0x8, %edx
0x100000e27 <+39>: movq %rdx, -0x58(%rbp)
0x100000e2b <+43>: movq %rcx, -0x60(%rbp)
0x100000e2f <+47>: movl %eax, -0x64(%rbp)
0x100000e32 <+50>: callq 0x100000f0e ; symbol stub for: memset
0x100000e37 <+55>: leaq -0x10(%rbp), %rcx
0x100000e3b <+59>: movq %rcx, %rdi
0x100000e3e <+62>: movl -0x64(%rbp), %esi
0x100000e41 <+65>: movq -0x58(%rbp), %rdx
0x100000e45 <+69>: callq 0x100000f0e ; symbol stub for: memset
; 将 rbp - 0x48 地址内容(即 1)移动到 rcx 寄存器
0x100000e4a <+74>: movq -0x48(%rbp), %rcx
0x100000e4e <+78>: movq %rcx, -0x8(%rbp)
; 将 rbp - 0x50 地址内容(即封装了 bar 的堆空间地址)移动到 rdx 寄存器
0x100000e52 <+82>: movq -0x50(%rbp), %rdx
; 将 rdx 寄存器内容(即封装了 bar 的堆空间地址)偏移 0x10(即 bar 的堆空间地址),结果存储在 rdx 寄存器
0x100000e56 <+86>: addq $0x10, %rdx
0x100000e5d <+93>: movq %rdx, -0x10(%rbp)
0x100000e61 <+97>: movq %rdx, %rdi
0x100000e64 <+100>: leaq -0x28(%rbp), %rsi
0x100000e68 <+104>: movl $0x21, %r8d
; 将 rdx 寄存器内容(即 bar 的堆空间地址)移动到 rbp - 0x70 地址
0x100000e6e <+110>: movq %rdx, -0x70(%rbp)
0x100000e72 <+114>: movq %r8, %rdx
0x100000e75 <+117>: movq -0x60(%rbp), %rcx
0x100000e79 <+121>: callq 0x100000f1a ; symbol stub for: swift_beginAccess
0x100000e7e <+126>: movq -0x48(%rbp), %rcx
; 再次将 rbp - 0x50 地址内容(即封装了 bar 的堆空间地址)移动到 rdx 寄存器
0x100000e82 <+130>: movq -0x50(%rbp), %rdx
; 将 rdx 寄存器内容(即封装了 bar 的堆空间地址)偏移 0x10(即 bar 的堆空间地址)与 rcx 寄存器内容(即 1)相加,结果存储在 rcx 寄存器
0x100000e86 <+134>: addq 0x10(%rdx), %rcx
0x100000e8a <+138>: seto %r9b
0x100000e8e <+142>: testb $0x1, %r9b
0x100000e92 <+146>: movq %rcx, -0x78(%rbp)
0x100000e96 <+150>: jne 0x100000ef0 ; <+240> at main.swift:9:13
0x100000e98 <+152>: movq -0x70(%rbp), %rax
0x100000e9c <+156>: movq -0x78(%rbp), %rcx
0x100000ea0 <+160>: movq %rcx, (%rax)
0x100000ea3 <+163>: leaq -0x28(%rbp), %rdi
0x100000ea7 <+167>: callq 0x100000f26 ; symbol stub for: swift_endAccess
0x100000eac <+172>: xorl %edx, %edx
0x100000eae <+174>: movl %edx, %ecx
0x100000eb0 <+176>: leaq -0x40(%rbp), %rax
0x100000eb4 <+180>: movl $0x20, %edx
0x100000eb9 <+185>: movq -0x70(%rbp), %rdi
0x100000ebd <+189>: movq %rax, %rsi
0x100000ec0 <+192>: movq %rax, -0x80(%rbp)
0x100000ec4 <+196>: callq 0x100000f1a ; symbol stub for: swift_beginAccess
; 将 rbp - 0x70 地址内容(即 bar 的堆空间地址)移动到 rax 寄存器
0x100000ec9 <+201>: movq -0x70(%rbp), %rax
; 将 rax 寄存器的值(即 0x000000000000000b)移动到 rax 寄存器
0x100000ecd <+205>: movq (%rax), %rax
0x100000ed0 <+208>: movq -0x80(%rbp), %rdi
; 将 rax 寄存器内容(0x000000000000000b)移动到 rbp - 0x88 地址
0x100000ed4 <+212>: movq %rax, -0x88(%rbp)
0x100000edb <+219>: callq 0x100000f26 ; symbol stub for: swift_endAccess
; 将 rbp - 0x88 地址内容(0x000000000000000b)移动到 rax 寄存器
0x100000ee0 <+224>: movq -0x88(%rbp), %rax
0x100000ee7 <+231>: addq $0x90, %rsp
0x100000eee <+238>: popq %rbp
0x100000eef <+239>: retq
0x100000ef0 <+240>: ud2
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK