我正在寻找将垃圾收集添加到Lua 5.1中的表(对象)的解决方案。我发现可以使用newproxy()
和__gc
来解决此问题:
Lua 5.1 workaround for __gc metamethod for tables https://github.com/katlogic/__gc
我不理解的是作者使用userdata作为表中的字段插入。
通过此包装器在其上设置了元表的所有对象都将被特殊键__gc_proxy(可以是任何字符串,用户可以通过__GC_PROXY全局定义)“污染”。如果要遍历表的字段(next(),pairs()...),则必须对它进行特殊处理。
和
使用建议的解决方案时,需要注意一件事-如果您通过pairs()遍历表,您将获得一个加法键。可以通过使用具有适当元方法的代理对象代替原始表来避免这种情况。
这是来自堆栈溢出线程的复制/粘贴示例:
function setmt__gc(t, mt)
local prox = newproxy(true)
getmetatable(prox).__gc = function() mt.__gc(t) end
t[prox] = true
return setmetatable(t, mt)
end
iscollected = false
function gctest(self)
iscollected = true
print("cleaning up:", self)
end
test = setmt__gc({}, {__gc = gctest})
collectgarbage()
assert(not iscollected)
for k, v in pairs(test) do
print(tostring(k) .. " " .. tostring(v))
end
输出为:
userdata: 0003BEB0 true
cleaning up: table: 00039D58
但是此清理是从脚本结尾而不是在collectgarbage()
的调用中进行的。
这可以通过以循环结尾的稍微修改的版本来证明。输出应为“清理中”:
function setmt__gc(t, mt)
local prox = newproxy(true)
getmetatable(prox).__gc = function() mt.__gc(t) end
t[prox] = true
return setmetatable(t, mt)
end
function gctest(self)
print("cleaning up:", self)
io.flush()
end
test = setmt__gc({}, {__gc = gctest})
collectgarbage()
while (true) do
end
相反,通过删除有问题的t[prox] = true
,该集合将按预期工作:
function setmt__gc(t, mt)
local prox = newproxy(true)
getmetatable(prox).__gc = function() mt.__gc(t) end
t[prox] = true
return setmetatable(t, mt)
end
function gctest(self)
print("cleaning up")
io.flush()
end
test = setmt__gc({}, {__gc = gctest})
collectgarbage()
while (true) do
end
输出:
cleaning up