12

Swift 拾遗 - 闭包

 3 years ago
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.
neoserver,ios ssh client

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 似乎在每次调用后并没有被销毁,原因其实是 bazbar 进行了「捕获」,接下来我们将仔细研究这一过程。

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK