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掉後還能訪問並修改外部變量的原因。

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