Lua元表和元方法
Lua语言中的每个值都可以有元表(metatable) ,但只有full userdata和table才能有实例元表,而其他类型的值则共享对应类型所属的同一个元表。
也就是说,可以为full userdata和table的实例单独设置不同的元表(Table和Udata结构体上拥有struct Table *metatable成员变量)。
在lua中只能给table设置元表,在c++中可以给所有类型设置元表。字符串标准库为所有string都设置了同一个元表,而其他类型默认是没有元表的。
一个table还可以把自己设成自己的元表,用于描述其自身特有的行为。
获取元表
类型 | lua示例 | c++示例 |
nil |
print(getmetatable(nil)) --nil |
lua_pushnil(L); // 将nil压入栈顶 int ret1 = lua_getmetatable(L, -1); // nil无元表,返回值ret1为0 |
boolean |
print(getmetatable(true)) --nil |
lua_pushboolean(L, 1); // 将boolean为true压入栈顶 int ret1 = lua_getmetatable(L, -1); // boolean无元表,返回值ret1为0 |
number |
print(getmetatable(3.14)) --nil print(getmetatable(100)) --nil |
lua_pushnumber(L, 3.14); // 将number(float)为3.14压入栈顶 int ret1 = lua_getmetatable(L, -1); // number无元表,返回值ret1为0 lua_pushinteger(L, 100); // 将number(int)为100压入栈顶 int ret2 = lua_getmetatable(L, -1); // number无元表,返回值ret2为0 |
string |
print(getmetatable("Hello World")) --table: 000001C47BA3FA80 |
lua_pushstring(L, "Hello World"); // 将string为Hello World压入栈顶 int ret1 = lua_getmetatable(L, -1); // string有元表,将元表压栈,返回值ret1为1 if (lua_istable(L, - 1)) // true { const void* p = lua_topointer(L, -1); // 元表p为00000299AEDC6590 } |
function |
print(getmetatable(print)) --nil |
lua_getglobal(L, "print"); // 获取全局函数print,并压入栈顶 int ret1 = lua_getmetatable(L, -1); // fuction无元表,返回值ret1为0 |
table |
print(getmetatable({})) --nil print(getmetatable(setmetatable({x=10},{}) )) --table: 000001C47D7F3820 |
lua_newtable(L); // 创建一个空表,并压入栈顶 int ret1 = lua_getmetatable(L, -1); // 空表无元表,返回值ret1为0 lua_newtable(L); // 创建一个空表,并压入栈顶 lua_pushstring(L, "x"); // 将string类型的键x压入栈顶 lua_pushinteger(L, 10); // 将整型的值10压入栈顶 lua_settable(L, -3); // 将表中的key为x的元素赋值为10,即:{x=10}。之后会将键和值都弹出栈 lua_newtable(L); // 创建一个空表,并压入栈顶 lua_setmetatable(L, -2);// 将刚创建的空表设置成索引为-2处的表的元表。之后将空表弹出栈 int ret1 = lua_getmetatable(L, -1); // 栈顶的表有元表,将元表压栈,返回值ret1为1 |
userdata |
print(getmetatable(io.stdin)) --table: 000001C47BA3FA00 |
lua_getglobal(L, "io"); // 获取全局表io,并压入栈顶 lua_pushstring(L, "stdin"); // 将string类型的键stdin压入栈顶 lua_gettable(L, -2); // 获取全局表io中键为stdio的userdata值,并压入栈顶 int ret1 = lua_getmetatable(L, -1); // 类型为userdata的io.stdin有元表,将元表压栈,返回值ret1为1 |
无 |
typedef struct Rect { float w, h; } Rect; Rect* ud1 = (Rect*)lua_newuserdata(L, sizeof(Rect)); //创建一个内存块为Rect的full userdata,并压入栈顶 int ret1 = lua_getmetatable(L, -1); // 栈顶的full userdata没有元表,返回值ret1为0 Rect* ud2 = (Rect*)lua_newuserdata(L, sizeof(Rect)); //创建一个内存块为Rect的full userdata,并压入栈顶 lua_newtable(L); // 创建一个空表,并压入栈顶 lua_setmetatable(L, -2);// 将刚创建的空表设置成索引为-2处的full userdata的元表。之后将空表弹出栈 int ret2 = lua_getmetatable(L, -1); // 栈顶的full userdata有元表,将元表压栈,返回值ret2为1 |
|
thread |
print(getmetatable(coroutine.create(function() end))) --nil |
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); int ret1 = lua_getmetatable(L, -1); // 栈顶的lua的mainthread没有元表,返回值ret1为0 lua_newthread(L); // 创建一个thread,并压入栈顶 int ret2 = lua_getmetatable(L, -1); // 栈顶的thread没有元表,返回值ret2为0
|
基础类型的元表保存在global_State的struct Table *mt[LUA_NUMTAGS]数组中(宏LUA_NUMTAGS为9)
string元表L->l_G->mt[LUA_TSTRING]中的内容如下:
table: 00000299AEDC6590 { [__index] => table: 000001E3CC037D80 { [gsub] => function: 00007FF779683FD0 { func: [C] -1 } [dump] => function: 00007FF779683BF0 { func: [C] -1 } [pack] => function: 00007FF779684A60 { func: [C] -1 } [reverse] => function: 00007FF779683110 { func: [C] -1 } [len] => function: 00007FF779682EA0 { func: [C] -1 } [lower] => function: 00007FF779683270 { func: [C] -1 } [match] => function: 00007FF779683DC0 { func: [C] -1 } [gmatch] => function: 00007FF779683E30 { func: [C] -1 } [char] => function: 00007FF779683A70 { func: [C] -1 } [find] => function: 00007FF779683D50 { func: [C] -1 } [unpack] => function: 00007FF779685520 { func: [C] -1 } [format] => function: 00007FF7796843D0 { func: [C] -1 } [byte] => function: 00007FF779683830 { func: [C] -1 } [rep] => function: 00007FF779683530 { func: [C] -1 } [packsize] => function: 00007FF779685340 { func: [C] -1 } [sub] => function: 00007FF779682F70 { func: [C] -1 } [upper] => function: 00007FF7796833D0 { func: [C] -1 } } }
① L->l_G->mt中默认只有string类型有元表,其他类型都为空
② 在c++中可以重新为string设置新的元表
③ 在c++中可以为nil、boolean、light userdata、number、function和thread类型设置元表,设置后L->l_G->mt中对应的Table指针会被更新
④ table、full userdata的元表是设置在实例上的,因此,其对应的L->l_G->mt数组中的Table指针一直为空
设置和取消元表
类型 | lua示例 | c++示例 |
table |
设置元表: local mt = {} local t1 = {x=10} setmetatable(t1, mt) -- 将mt表设置成t1表的元表 -- 将mt表设置成{x=10}表的元表,并将结果赋值给t2表。等价于上面两句代码 local t2= setmetatable({x=10},mt) 取消元表:
setmetatable(t1, nil) -- 取消t1表的元表 |
设置元表: lua_newtable(L); // 创建一个空表a并压栈 lua_pushstring(L, "x"); // 把字符串x压栈 lua_pushinteger(L, 10); // 把整型数10压栈 lua_rawset(L, -3); // 设置索引为-3处的表a的key(在栈顶下一个,即x)对应的value为10(在栈顶处)【不触发元方法】 设置成功后,将栈顶的2个元素都出栈 lua_newtable(L); // 创建一个空表b并压栈 lua_setmetatable(L, -2); // 将栈顶的空表b设置为索引为-2处表a的元表,并将栈顶的1个元素出栈
取消元表: lua_pushnil(L); // 把nil压栈 lua_setmetatable(L, -2); // 将索引为-2处表的元表设置为nil(取消其元表),并将栈顶的1个元素出栈 |
full userdata | No |
设置元表: lua_newuserdata(L, 20); //创建一个内存块大小为20字节的full userdata,并压栈 lua_newtable(L); // 创建一个空表a并压栈 lua_setmetatable(L, -2); // 将栈顶的空表a设置为索引为-2处full userdata的元表,并将栈顶的1个元素出栈
取消元表: lua_pushnil(L); // 把nil压栈 lua_setmetatable(L, -2); // 将索引为-2处full userdata的元表设置为nil(取消其元表),并将栈顶的1个元素出栈
|
Lua语言中的每种类型的值都有一套可预见的操作集合。如:数字相加,字符串拼接等。但无法将table与其他类型相加。但可借助元表及其元方法,可以预先定义一系列操作的行为,如:运算符重载等。
例如:当Lua试图将table与其他类型相加时,会先检查两者之一是否有元表及该元表是否有__add元方法(优先检查第1个加数的;第1个加数没有,才检查第2个加数的)。如果有,则调用找到的__add元方法来完成加法操作。
local tb1 = {x=1} local mt1 = {} mt1.__add = function (a,b) return 10 end -- print(tb1 + 1) -- Exception has occurred: attempt to perform arithmetic on a table value (local 'tb1') setmetatable(tb1,mt1) print(tb1 + 'hello') -- 输出10 print(tb1 + {y=2}) -- 输出10 print(3.2 + tb1) -- 输出10 local tb2 = setmetatable({z=3}, {__add = function (a,b) return 20 end}) print(tb1 + tb2) -- 输出10 print(tb2 + tb1) -- 输出20
注:无论调用的是tb1或者tb2元表中的__add,__add的第一形参永远对应左操作数,第二形参永远对应右操作数。换言之,__add形参顺序与参与”+”运算的操作数顺序相同,而与使用左右操作数的哪一个元表中实现了__add无关。
算术运算相关元方法
元方法 | 说明 | 运算符类型 |
__add(+) | 加法 | 二元运算符:function(a, b) |
__sub(-) | 减法 | 二元运算符:function(a, b) |
__mul(*) | 乘法 | 二元运算符:function(a, b) |
__div(/) | 除法 | 二元运算符:function(a, b) |
__idiv(//) | floor除法(即:向下取整除法) | 二元运算符:function(a, b) |
__mod(%) | 取模 | 二元运算符:function(a, b) |
__pow(^) | 幂运算 | 二元运算符:function(a, b) |
__unm(-) | 取负数 | 一元运算符:function(a) |
__band(&) | 按位与 | 二元运算符:function(a, b) |
__bor(|) | 按位或 | 二元运算符:function(a, b) |
__bxor(~) | 按位异或 | 二元运算符:function(a, b) |
__bnot(~) | 按位取反 | 一元运算符:function(a) |
__shl(<<) | 向左移位 | 二元运算符:function(a, b) |
__shr(>>) | 向右移位 | 二元运算符:function(a, b) |
__concat(..) | 连接运算 | 二元运算符:function(a, b) |
__add(+)代码示例
local mt = {} mt.__add = function (a, b) local result = {} local size = math.max(#a, #b) for i=1, size do result[i] = (a[i] or 0) + (b[i] or 0) end return result end local t1 = {10, 20, 30} setmetatable(t1, mt) local t2 = {2, 4} local t3 = t1 + t2 --t3 = {12, 24, 30}
__unm(-)代码示例
local function Unm(a) local result = {} for i=1, #a do result[i] = -1 * (a[i] or 0) end return result end local mt = {__unm = Unm} local t1 = {13, 20, 0, 30, 40} setmetatable(t1, mt) local t2 = -t1 --t2 = {-13, -20, 0, -30, -40}
__band(&)代码示例
local function Band(a, b) local result = {} local size = math.max(#a, #b) for i=1, size do result[i] = (a[i] or 0) & (b[i] or 0) end return result end local mt = {__band = Band} local t1 = {10, 20, 0, 30, 40} setmetatable(t1, mt) local t2 = {2, 4, 6, 0} local t3 = t1 & t2 --t3 = {2, 4, 0, 0, 0}
__bnot(~) 代码示例
local function Bnot(a) local result = {} for i=1, #a do result[i] = ~(a[i] or 0) end return result end local mt = {__bnot = Bnot} local t1 = {13, 20, 0, 30, 40} setmetatable(t1, mt) local t2 = ~t1 --t2 = {-14, -21, -1, -31, -41}
__shl(<<)代码示例
local function Shl(a, b) local result = {} for i=1, #a do result[i] = (a[i] or 0) << b end return result end local mt = {__shl = Shl} local t1 = {2, 4, 6, 0} setmetatable(t1, mt) local t2 = t1 << 2 --t2 = {8, 16, 24, 0}
__concat(..)代码示例
local function Concat(a, b) local result = {} local size = math.max(#a, #b) for i=1, size do result[i] = math.tointeger((a[i] or 0) .. (b[i] or 0)) end return result end local mt = {__concat = Concat} local t1 = {10, 20, 0, 30, 40} setmetatable(t1, mt) local t2 = {2, 4, 6, 0} local t3 = t1 .. t2 --t3 = {102, 204, 6, 300, 400}
关系运算相关元方法
元方法 | 说明 | 型如 |
__eq(==) |
是否相等 注:没有~=元方法,lua会将a~=b转换为not(a==b) |
二元运算符:function(a, b) |
__lt(<) |
是否小于 注:没有>元方法,lua会将a>b转换为b<a |
二元运算符:function(a, b) |
__le(<=) |
是否小于等于 注1:没有>=元方法,lua会将a>=b转换为b<=a 注2:a<=b并不能转换为not(b<a) 正常情况下,是可以转换的。但当a或b为nan时,则不成立。 因为有nan数参与的比较,结果都是false local a = 0.0 local b = 0.0 local c = a / b -- c为nan if c <= a then -- 条件不满足 print("c <= a") end if not (a < c) then print("not (a < c)") -- 条件满足,打印出:not (a < c) end
|
二元运算符:function(a, b) |
__le(<=)代码示例
local function LessEqual(a, b) local size = math.max(#a, #b) for i=1, size do if (a[i] or 0) > (b[i] or 0) then return false end end return true end local mt = {__le = LessEqual} local t1 = {1, 4, 5, -1, 0} setmetatable(t1, mt) local t2 = {2, 4, 6, 0} local ret = t1 <= t2 --ret = true
表访问相关元方法
元方法 | 说明 | 型如 |
__index(读) |
访问表某个不存在的字段时,当该表设置了元表,将触发元表的__index元方法(若存在)调用并返回结果 注1:访问表存在的字段,直接返回表中该字段的值,不会触发__index元方法 注2:__index可以一个function(table, key),也可以为一个table。 为function时,参数table为当前访问的表,key为当前访问的键 为table时,可使用当前访问的键key,从该table中取得key对应的值 注3:如果不想触发__index元方法来读取字段的值,lua中可使用rawget(table, key),c++中可使用int lua_rawget (lua_State *L, int index) |
function(table,key) |
table | ||
__newindex(写) |
设置表某个不存在的字段时,当该表设置了元表,将调用元表的__newindex元方法(若存在)来处理 注1:设置表存在的字段,直接设置表中该字段的值,不会触发__newindex元方法 注2:__newindex可以一个function(table, key, value),也可以为一个table。 为function时,参数table为当前设置的表,key为当前设置的键, value为当前设置的值 为table时,设置不存在的字段时,表自身并不会被赋值,而会对其元表的__newindex指向的表中该字段进行赋值 注3:如果不想触发__newindex元方法来读取字段的值,lua中可使用rawset(table, key, value),c++中可使用void lua_rawset (lua_State *L, int index) |
function(table,key,value) |
table |
__index为function的代码示例
local mt = {intdefault=100} mt.__index = function (t, k) -- t为当前访问的表,k为当前访问的键 if math.type(k)=='integer' then -- k为整数时 return mt.intdefault end return "lua" end local t = {1, 8, h1="earth", 5, h2="sun"} setmetatable(t, mt) print(t['h1']) -- earth print(t[3]) -- 5 print(t.h2) -- sun print(t[4]) -- 100 print(t.h3) -- lua
__index为table的代码示例
local base = { ["h1"] = "mars", [2] = 100, ["name"] = "chen" } local mt = {__index = base} local t = {1, 8, h1="earth", 5, h2="sun"} setmetatable(t, mt) print(t.h1) -- earth print(t[1]) -- 1 print(t.h2) -- sun print(t[2]) -- 8 print(t.name) -- chen print(t.h3) -- nil
__newindex为function的代码示例
local mt = {intdefault=100} mt.__newindex = function (t, k, v) -- t为当前设置的表,k为当前设置的键,v为当前设置的值 if math.type(k)=='integer' then -- k为整数时 return rawset(t, k, mt.intdefault) -- 不触发__newindex元方法来设置t[k]=mt.intdefault end return rawset(t, k, 'lua') -- 不触发__newindex元方法来设置t[k]='lua' end local t = {1, 8, h1="earth", 5, h2="sun"} setmetatable(t, mt) t['h1'] = "mars" print(t['h1']) -- mars t[3] = 10 print(t[3]) -- 10 t[4] = 20 print(t[4]) -- 100 t.h3 = "moon" print(t.h3) -- lua
__newindex为table的代码示例
local base = { ["h1"] = "mars", [2] = 100, ["name"] = "chen" } local mt = {__newindex = base} local t = {1, 8, h1="earth", 5, h2="sun"} setmetatable(t, mt) t['h1'] = "moon" print(t['h1'], base["h1"]) -- moon mars t[3] = 10 print(t[3], base[3]) -- 10 nil t[4] = 20 print(t[4], base[4]) -- nil 20 注:t[4]并没有赋值成20,而是其元表的__newindex指向的表的base[4]被赋值成20 t.h3 = "sature" print(t.h3, base.h3) -- nil sature 注:t.h3并没有赋值成"sature",而是其元表的__newindex指向的表的base.h3被赋值成"sature"
其他元方法
元方法 | 说明 | 型如 |
__tostring |
转换为字符串 在lua中调用tostring(a)和print(a)函数,将触发a的元表的__tostring元方法调用 |
1个参数:function(a) |
__len(#) |
取长度 缺省情况下,对table取#,得到的是数组元素(不含末尾为nil的元素)的个数(不包含键值对) local t1 = {10,20,nil,30} -- #t1为4 local t2 = {10,20,nil} -- #t2为2 local t3 = {nil,10,20} -- #t3为3
可以通过实现__len元方法,来重新定义#运算符的行为 |
1个参数:function(a) |
__metatable | 用于保护元表。getmetatable时返回__metatable的内容,setmetatable直接引发异常 | |
__name | 自定义对象的类型名称 | |
__pairs | 迭代器 | 2个参数:function(a, b) |
__call | 实现talbe的()运算符。可以用来实现类似构造方法的调用形式。 | N个参数:function(a, ...) |
__gc | 该元方法称为终结器(finalizer)。用于垃圾回收该Lua对象之前,做一些额外的资源管理工作 (如:关闭文件、网络或数据库连接,或是释放一些自分配的内存)。 | 1个参数:function(a) |
__mode |
__mode用于设置表的key、value为弱引用(weak reference)。对应的表也被称为弱表(weak table)。 当key、value为gc管理对象(function、table等),且只被弱引用所引用,则会被gc回收。 3种弱引用的表: ① 具有弱引用key的table local t={} setmetatable(t, {__mode="k"}) -- t为弱引用key的table ② 具有弱引用value的table local t={} setmetatable(t, {__mode="v"}) -- t为弱引用value的table ③ 同时具有弱引用key和弱引用value的table local t={} setmetatable(t, {__mode="kv"}) -- t为具有弱引用key和弱引用value的table 任何情况下,只要key和value中的一个被gc,那么这个key-value对,就会被从表中移除 |
__tostring代码示例
local function ToString(a) local result = "" local size = #a for i=1, size do result = result .. (a[i] or 0) if i~=size then result = result .. "," end end return result end local mt = {__tostring = ToString} local t = {1, 4, 5, -1, 0} setmetatable(t, mt) print(t) -- 打印出:1,4,5,-1,0 local s = tostring(t) -- s = '1,4,5,-1,0'
__len(#)代码示例
local function Length(a) local len = 0 for _,_ in pairs(a) do len = len + 1 end return len end local mt = {__len = Length} local t = {1, 8, h1=nil, h2="earth", nil, 5, h3="sun"} print(#t) -- 打印出:4 setmetatable(t, mt) print(#t) -- 打印出:5
__metatable代码示例
local tb1 = {x=1} local mt1 = {} mt1.__metatable = "not your business." setmetatable(tb1, mt1) print(getmetatable(tb1)) -- not your business. -- setmetatable(tb1, {}) -- Exception has occurred: cannot change a protected metatable --引发错误
__name代码示例
local t =setmetatable({}, {__name = "MyObject"}) -- 下面代码会导致异常。因为表t在其元表中没有实现__concat元方法 local s = t.."go" --Exception has occurred: test.lua:5: attempt to concatenate a MyObject value (local 't')
__pairs代码示例
local default = { ["name"] = "chen", ["id"] = 7, ["age"] = 23, ["school"] = "whu" } local func = function (t, k) local nk, nv = next(default, k) -- 返回default表的下一个元素的键和值 if nk and t[nk] then -- 如果键不为nil,且在t表中存在 nv = t[nk] -- 将t表对应的值赋值给nv end return nk, nv end local test = {["id"] = 8} -- 以下for循环,打印出如下内容: -- id 8 for k, v in pairs(test) do print(k, v) end print("---------------------------") local mt = {} mt.__pairs = function (t, k) return func, t, nil end setmetatable(test, mt) -- 以下for循环,打印出如下内容: -- name chen -- age 23 -- id 8 -- school whu for k, v in pairs(test) do print(k, v) end
__call代码示例
local t = setmetatable({x=100}, {__call = function (tb, ...) print("test __call! args:", tb, tb.x, ...) end}) print(t) --table: 0000021A82720D60 t(1,2,3,4,5) --test __call! args: table: 0000021A82720D60 100 1 2 3 4 5
__gc代码示例
local t = setmetatable({x=100}, {__gc = function (tb) print("test __gc! args:", tb, tb.x) end}) print(t) --table: 000001BD61EA4A50 t = nil -- 强制执行垃圾回收。会触发元方法__gc collectgarbage() --test __gc! args: table: 000001BD61EA4A50 100
__mode="k"代码示例
a = {} -- a为一个全局表。不会被gc回收。 local k1 = {} local v1 = {} a[k1] = v1 local k2 = {} local k3 = {} local v2 = {} a[k2] = 100 a[k3] = 200 a[1] = v2 a[2] = {} a[3] = function() end a[4] = "hello" a[5] = 300 v1 = nil k2 = nil v2 = nil k3 = nil collectgarbage() -- 强制执行垃圾回收 -- 以下for循环,打印出如下内容: -- 1 table: 000002381FB4B2B0 -- 2 table: 000002381FB4B0B0 -- 3 function: 000002381FAFB7C0 -- 4 hello -- table: 000002381FB4A830 100 -- table: 000002381FB4A6B0 table: 000002381FB49870 -- 5 300 -- table: 000002381FB4AFB0 200 for i, v in pairs(a) do print(i, v) end print("---------------------------") setmetatable(a, {__mode = "k"}) -- 将key设置为弱引用 collectgarbage() -- 强制执行垃圾回收 -- 以下for循环,打印出如下内容: -- 1 table: 000002381FB4B2B0 -- 2 table: 000002381FB4B0B0 -- 3 function: 000002381FAFB7C0 -- 4 hello -- table: 000002381FB4A6B0 table: 000002381FB49870 -- 5 300 for i, v in pairs(a) do print(i, v) end
__mode="v"代码示例
a = {} -- a为一个全局表。不会被gc回收。 local k1 = {} local v1 = {} a[k1] = v1 local k2 = {} local k3 = {} local v2 = {} a[k2] = 100 a[k3] = 200 a[1] = v2 a[2] = {} a[3] = function() end a[4] = "hello" a[5] = 300 v1 = nil k2 = nil v2 = nil k3 = nil collectgarbage() -- 强制执行垃圾回收 -- 以下for循环,打印出如下内容: -- 1 table: 000002381FB4B2B0 -- 2 table: 000002381FB4B0B0 -- 3 function: 000002381FAFB7C0 -- 4 hello -- table: 000002381FB4A830 100 -- table: 000002381FB4A6B0 table: 000002381FB49870 -- 5 300 -- table: 000002381FB4AFB0 200 for i, v in pairs(a) do print(i, v) end print("---------------------------") setmetatable(a, {__mode = "v"}) -- 将key设置为弱引用 collectgarbage() -- 强制执行垃圾回收 -- 以下for循环,打印出如下内容: -- 4 hello -- 5 300 -- table: 000002381FB4A830 100 -- table: 000002381FB4AFB0 200 for i, v in pairs(a) do print(i, v) end
__mode="kv"代码示例
a = {} -- a为一个全局表。不会被gc回收。 local k1 = {} local v1 = {} a[k1] = v1 local k2 = {} local k3 = {} local v2 = {} a[k2] = 100 a[k3] = 200 a[1] = v2 a[2] = {} a[3] = function() end a[4] = "hello" a[5] = 300 v1 = nil k2 = nil v2 = nil k3 = nil collectgarbage() -- 强制执行垃圾回收 -- 以下for循环,打印出如下内容: -- 1 table: 000002381FB4B2B0 -- 2 table: 000002381FB4B0B0 -- 3 function: 000002381FAFB7C0 -- 4 hello -- table: 000002381FB4A830 100 -- table: 000002381FB4A6B0 table: 000002381FB49870 -- 5 300 -- table: 000002381FB4AFB0 200 for i, v in pairs(a) do print(i, v) end print("---------------------------") setmetatable(a, {__mode = "kv"}) -- 将key设置为弱引用 collectgarbage() -- 强制执行垃圾回收 -- 以下for循环,打印出如下内容: -- 4 hello -- 5 300 for i, v in pairs(a) do print(i, v) end
参考
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)