弱表教程 |
|
在像 Lua 这样的使用垃圾回收的计算机语言中,如果对对象的引用不会阻止对象的回收,则该引用被称为弱引用。弱引用对于确定对象何时被回收以及缓存对象而不阻碍其回收非常有用。
Lua 并没有提供对单个弱引用的接口,而是提供了一个更高级的结构,称为弱表。在弱表中,键和/或值是弱引用。如果弱表的键或值被回收,该表的对应项将被删除。
以下是一个完整的,尽管非常人为的,弱表实际应用的示例
t = {} setmetatable(t, { __mode = 'v' }) do local someval = {} t['foo'] = someval end collectgarbage() for k, v in pairs(t) do print(k, v) end
尝试在注释掉collectgarbage()
调用和不注释的情况下运行此示例。注释掉调用后,程序将不会打印任何内容,因为唯一表项的值将被回收。
弱表是通过将其元表设置为具有__mode
字段的元表来创建的。在上面的示例中,我们使用v
启用弱值(类似地,k
用于键)。创建测试值someval
作为do
块中的局部变量的目的是为了能够通过调用collectgarbage
来强制稍后回收该值。请注意,使用字符串或数字之类的字面量作为someval
将不起作用,因为字面量永远不会被垃圾回收。
请注意,一旦一个表被用作元表,就不能再更改元表的__mode
字段(有关确切的限制,请参阅参考手册 [1] 中的第 2.10.2 节)。换句话说,以下代码试图通过更改元表的__mode
字段来将一个表更改为弱表,这是错误的
getmetatable(t).__mode = 'v'
__mode
字段。因此,以下风格,它创建一个空表,然后立即赋予它一个元表,是正确的
local weaktable = setmetatable({}, {__mode="k"})
弱表通常用于希望在不改变值的情况下对其进行注释的情况。例如,您可能希望给对象一个名称,该名称可以在打印对象时使用。在这种情况下,您不希望对象被命名的事实阻止其被垃圾回收,因此您希望使用一个键(对象)为弱的表。
local names = setmetatable({}, {__mode = "k"}) -- with the example below, this would be a local function function name(obj, str) names[obj] = tostring(str) return obj end -- keep the original print function available local _print = print function print(...) for i = 1, arg.n do local name = names[arg[i]] if name then arg[i] = name end end _print(unpack(arg)) end
您可能希望将其用于调试,通过自动命名全局变量。您可以通过向全局表添加一个简单的元方法(参见 元方法教程)来做到这一点
local globalsmeta = {} local nameable_type = {["function"] = true, userdata = true, thread = true, table = true} function globalsmeta:__newindex(k, v) if nameable_type[type(v)] then name(v, k) end rawset(self, k, v) end setmetatable(_G, globalsmeta)
请注意,我们通过使用常量表对值类型进行单次检查,避免了执行一系列复杂的if then elseif...
语句。
对于高级教程读者,__newindex
函数的文本可以写成如下形式
rawset(self, k, nameable_type[type(v)] and name(v, k) or v)