本文首发于我的个人博客
前言
先回顾一下,上一篇 Swift之闭包(Closure)中对闭包的解释
- 一个函数和它所捕获的变量\常量环境组合起来,称为闭包
- 一般指定义在函数内部的函数
- 一般它捕获的是外层函数的局部变量\常量
- 可以把闭包想象成是一个类的实例对象
- 内存在堆空间
- 捕获的局部变量\常量就是对象的成员(存储属性)
- 组成闭包的函数就是类内部定义的方法
问题
先看下面一段代码,猜猜会输出什么
1 | typealias Fn = (Int) -> Int |
结果是输出
1 | 1 |
那么,问题来了,为什么输出的是10呢?因为按照常识,var num = 0 是局部变量,执行完就销毁了,怎么能再后面继续使用呢?
验证
我们先从简单的说起
首先是下面一端代码
1 | typealias Fn = (Int) -> Int |
先不适用num ,直接 return i 并在这里打断点,结果如下
1 | testSwift`getFn(): |
可知,0xd(%rip), %rax 这段代码,把地址值,也就是getFn() 函数的地址值给了rax,
根本没有alloc malloc等代码,也就是说,没有开辟堆空间。那么接下来我们看下面的代码
1 | typealias Fn = (Int) -> Int |
断点如下
1 | testSwift`getFn(): |
进一步验证,下面的代码是因为,写文章的时候,重新跑了一遍,所以函数 getFn() 函数的抵制和截图不一致,是
rax = 0x0000000101849fd0
这次我们在
1 | typealias Fn = (Int) -> Int |
因为调用了三次 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 | 1 |
关于汇编的调试指令可以参考