为什么在 Lua 5.1 中将 userdata 对象添加到表中的 __gc 解决方法中?

我正在寻找在 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() 遍历表,则会获得一个附加键。可以通过使用带有正确元方法的代理对象来替代原始表来避免它。

这是从 Stack Overflow 线程复制/粘贴的一个示例:

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() 开始的。

这可以通过稍微修改版本来演示。输出应为“cleaning up”:

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
  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
点赞