Weak Tables Tutorial |
|
在像 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 字段(有关确切限制,请参见参考手册第 2.10.2 节 [1])。换句话说,以下代码试图通过更改其元表的 __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
您可以使用此功能通过自动命名全局变量来调试。您可以通过向 globals 表添加一个简单的元方法(参见 MetamethodsTutorial)来实现这一点。
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)