Swift 闭包底层原理

5,340 阅读2分钟

1. 定义

网上有各种关于闭包的定义,个人觉得比较严谨的定义是:

  1. 在一个函数内部,另一个函数和它所捕获的变量\常量环境组合起来,整体称为闭包。
  • 一般定义在函数内部的函数。
  • 一般捕获的是外层函数的局部变量\常量。

2. 猜测

Swift的闭包,其本质会不会跟OC的闭包一样,也是一个对象呢?

如果能够证明同时满足以下两点,那么就可以证明,闭包的本质,就是一个对象:

  1. 开辟了堆空间。
  2. 并且它的内存结构跟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到堆空间,封装成对象,并进行内存管理。