闭包定义:一个函数和它捕获的变量、常量环境组合起来,称为闭包。(但是好多人会把闭包表达式简称闭包,但是其实是两回事)
1、一般指定义在函数内部的函数
2、一般捕获的是外层函数的局部变量、常量
我们在函数里面定义一个函数,使用汇编看一看内层函数是怎样捕获外层函数的局部变量
首先写一个函数里面嵌套一个函数里面的汇编是什么样
定义一个函数嵌套函数,打上断点我们定义一个exec()函数返回值是一个()-> Int类型的函数,并且初始化一个函数,赋值个全局变量fn。
断点定位位置上面的汇编可以看到是在exec()里面,首先我们要了解在汇编里面rax寄存器与rdx寄存器是作为函数的返回值,这里没有rdx,但是有edx。edx其实就是就是在rdx寄存器里面,rdx寄存器是64bit,edx是32bit。所以根据上面的可以看出rax里面放置的是我们定义sum的函数地址(右面的注释也是可以看出来的),edx里面放的是0,因为上面 做了异或的操作,异或相同的为0,所以ecx里面是0.把ecx放到rdx里面。
放开断点进入到mian函数里面
在控制台输入si一步一步就会到这里call是调用exec()函数使用rax寄存器、rdx寄存器作为返回参数,根据右面的注释可以看出来,rax寄存器里面的sum函数地址放入fn的内存中,rdx寄存器里面的内容放入到fn的后8个字节。所以fn的内存中存放的是sum的函数地址和8个0.
这是普通的情况下,内部函数没有对外部函数的局部变量进行捕获。接下来举一个捕获的例子
内层函数捕获外层函数的局部变量例子这个例子跟上一个例子有些不同,不同点在于num += 1写到了sum函数中,而且调用的三次fn(),在控制台可以看出打印的结果,是11、12、13。我对num并没有做什么,但是打印的结果是不一样的,这说明第二次调用的fn中的num是使用上一次fn中的num。
在一个函数中局部变量是放在栈中,走到了最后一个大括号是会被释放掉。但是调用了几次num都是累加的,说明num是没有被释放掉。num应该是没有存放在栈中,但是应该存放在哪里呢
下面我们来打个断点看一下汇编代码
exec的汇编代码首先我们看一下函数的返回值,rax寄存器和rdx寄存器是函数的发返回参数,rax右面的主是可以看到是函数sum的地址值。
exec的汇编代码rdx是从-0x10(%rbp)里面取的值,-0x10(%rbp)里面的值又是从allocObject里面返回的值,在程序里面,带alloc字样的注释,是从堆空间分配的内存空间。这里面看到立即数$0xa,这个就是我们初始化的10,存放到rax + 0x10的位置,有人就会问为什么会是加上0x10,而不是直接放到rax里面呢,因为在堆空间开辟的内存空间里是有16个字节是存放一些其他的信息,前8个字节是放的类型信息,后8个字节是存放的有关引用计数的信息。所以这里不难发现rdx里面存放的是num的内存地址。
下面我们返回main函数看一下,exec()函数的返回值放到哪里
main函数的汇编代码从汇编里面看rax里面的内容是放在fn的前8个字节里面,rdx的内容是放在fn的后8个字节里面。
rax是存放的sum函数的函数地址,rdx是存放的num的堆空间的地址。
总结:在内部函数没有使用外部函数的情况下,num是在栈空间里面存储,变量fn也不会对num进行存储。如果 内部函数对外部变量进行引用,num就会在堆空间进行开辟空间,内存地址由fn进行存储。闭包是通过新开辟了一个堆空间进行值的存储,把堆空间的地址值存放到变量的后八个字节中进行存储。
网友评论