Lua 源码分析之闭包Closure

        闭包是Lua语言编程一个重要而又常用的概念。它主要作用是在函数离开作用域后还可以访问外部的临时变量,这些变量称为upvalue。
        闭包分为两种,分别CClosure和LClosure。它们都被封装到一个Closure结构体里,CClosure和LClosure都有一个ClosureHeader的结构体。结构体ClosureHeader的字段作用:
        1.isC: 区分是哪一种闭包类型。0是LClosure, 1是CClosure
        2.nupvalues:记录upvalue的数量。

#define ClosureHeader \
	CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
	struct Table *env

typedef struct CClosure {
  ClosureHeader;
  lua_CFunction f;
  TValue upvalue[1];
} CClosure;


typedef struct LClosure {
  ClosureHeader;
  struct Proto *p;
  UpVal *upvals[1];
} LClosure;


typedef union Closure {
  CClosure c;
  LClosure l;
} Closure;

这里我们主要分析LClosure的情况。例如以下创建闭包的代码

function test()
	local a = 1
	local function callback()
		print(a)
	end
	return callback
end
local cb = test()
cb()

    在调用test函数时,创建一个local变量和一个callback函数,callback可以访问外部的a。当调用test的时候,此时栈上会创建a,然后创建callback这个闭包。
创建闭包的过程:
    1.获取函数开始到闭包创建前的upvalue数量,这里只有a,数量为1.
    2.创建闭包会根据upvalue的数量来创建,upvalue的数量会影响闭包的创建大小。
    3.闭包LClosure创建好了之后,需要去创建UpValue,即闭包内可以访问的外部变量,UpValue会和openvalue链成一条双向链表,此时LuaState的openvalue会指向最新的UpValue,UpValue为open 状态。open 状态指的是:upvalue的值还在栈里,当闭包要从栈里pop掉的时候,UpValue会变成close状态(稍后再说)。
    4.创建闭包的时候,这个时候内存图为:

        假如现在test函数执行完毕,返回callback函数,这时候栈会把LClosure Pop出来。Pop出来的时候,LClosure指向的UpValue会把指向的值设置到自己的结构体上,并把UpValue指向自己结构TValue上,此时即为Close状态。同时UpValue会从openvalue的链表上移除。此时的内存图为:

    闭包和变量从栈中移除后,闭包还能访问原来的变量,但是它并不在栈上,而是被拷贝闭包一直指向的UpValue去,这就是为什么闭包和变量被Pop掉后还能访问并修改外部变量的原因。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章