可可西

Lua元表和元方法

Lua语言中的每个值都可以有元表(metatable) ,但只有full userdata和table才能有实例元表,而其他类型的值则共享对应类型所属的同一个元表。

也就是说,可以为full userdata和table的实例单独设置不同的元表(TableUdata结构体上拥有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

 

参考

Lua 元表(Metatable)

 

posted on 2022-09-11 15:05  可可西  阅读(392)  评论(0编辑  收藏  举报

导航