1. 定义
网上有各种关于闭包的定义,个人觉得比较严谨的定义是:
- 在一个函数内部,另一个函数和它所捕获的变量\常量环境组合起来,整体称为闭包。
- 一般定义在函数内部的函数。
- 一般捕获的是外层函数的局部变量\常量。
2. 猜测
Swift的闭包,其本质会不会跟OC的闭包一样,也是一个对象呢?
如果能够证明同时满足以下两点,那么就可以证明,闭包的本质,就是一个对象:
- 开辟了堆空间。
- 并且它的内存结构跟class一样。
3. 证明开辟了堆空间
如果调了alloc,最终调了malloc函数,那么就说明向堆空间申请了内存。
1. 打断点,看函数getNumber内部是如何实现的。
2. 进入汇编,继续进入getNumber函数。
3. 继续进入swift_allocObject函数。
4. 继续进入swift_showAlloc函数,可以看出,确实调了malloc_zone_malloc函数,那由此可以证明,确实在堆区开辟了空间。
4. 证明闭包的内存结构跟类一样
要想证明闭包的内存结构跟类一样,那就需要先找出刚创建好的对象,然后打印出它的内存地址,然后查看内存地址中存储着什么,然后跟类的内存结构对比,从而判断出闭包的内存结构。
1. 依旧是这份代码,打开反汇编。
2. 第6行,注释写的很清楚,调用getNumber函数,继续跟进去。
3. 发现这个函数 swift_allocObject 刚分配完空间,那是不是刚申请完堆空间的闭包,打印一样返回值 rax。
4. 打印rax的内存地址,并打印出这个内存地址的内存结构。
前8个字节存储着类型信息,然后引用计数,然后成员变量。
内存结构跟class一模一样。
由此可以判断出,闭包的本质就是一个可实例的对象。
用法是不是跟类的实例对象很像很像。
5. 总结
其实Swift的闭包跟OC的Block,其本质是差不多的。
从汇编可以看出,闭包捕获局部变量,是调用了malloc函数, 开辟了堆空间,调用malloc所开辟出来的空间,打印其内存地址,窥探它的内存结构,发现是类的内存结构,就足以说明闭包的本质,是一个对象。
第一条是类信息,第二条存储的是引用计数,第三条是存储的局部变量的地址。
在函数内的函数,捕获了外部函数的局部变量,将外部函数的局部变量从栈空间copy到堆空间,封装成对象,并进行内存管理。