在Lua中通过引用删除变量

时间:2014-10-01 09:47:42

标签: memory lua reference lua-table

我在几个表中有几个对象。 多个功能改变并将对象切换到其他功能。

让我们说我的表是这样的:

objectTable = {obj1, obj2, obj3}
otherobjTable = {objA, objB, objC, objD}

让我们说这些是在main.lua初始化的。

现在,当跟踪obj1时,它被一个函数改变了,它改变了它并给出了另一个函数的引用,这又改变了它。 一步可能看起来像:

function()
   if something then func(obj_1)
   elseif something else then func(obj_2)
   elseif something other then func(obj_3)
   //... and so on...
end

function func(received_Object)
  if something then
    table.insert(received_Object, a value)
  end
  callAnotherFunction(received_Object)
end

function callAnotherFunction(received_Object)
  if input == "Delete it" then
    local name = received_Object.name
    received_Object = nil
    return string.format("%s was deleten", name)
  else
    return false
  end
end

现在的问题是,在 received_Object = nil 之后,参考点为零,但对象仍然存在。如何确保将其删除?

1 个答案:

答案 0 :(得分:3)

在Lua中,某些类型(如表)总是通过引用传递,而其他类型(如数字)总是按值传递。

此外,Lua是一种由垃圾收集器管理内存的语言。当没有更多引用(让它们称之为锚点)时,垃圾收集器会删除一个对象(例如一个表)。

现在这段代码:

local t = {}
local t1 = {t}
someFunc(t)

为该表创建三个锚点。当someFunc将另一个表作为参数传递给另一个函数时,将创建第四个锚(以该函数的局部变量/参数的形式)。

为了让垃圾收集器扫描第一个表,所有这些引用必须消失(通过分配nil或超出范围)。

了解一下,当您将nil分配给本地t时,这并不意味着该表将被删除。更不用说所有对该表的引用都将失效。这意味着你只需释放这一个锚点,这只是当时的四个锚点之一。

可能的解决方案

一种可能的解决方案是传递包含对象的表以及存储对象的索引/键:

function func(myTable, myKey)
...
end

现在,如果在此功能中执行此操作:

myTable[myKey] = nil

(并且没有其他锚点被创建),键下的对象将不再有指向它的引用,并且将在下一轮被垃圾收集器标记为扫描。当然,callAnotherFunction也必须以同样的方式进行修改:

callAnotherFunction(myTable, myKey)
...
end

如果对这些函数中的对象执行许多操作,则可以将其缓存到局部变量中以避免多次表查找。这没关系,因为当函数完成时,锚将与局部变量一起被清除:

callAnotherFunction(myTable, myKey)
    local myObj = myTable[myKey]
    ...
    if myCondition then myTable[myKey] = nil end
end  --here myObj is no longer valid, so the anchor is gone.

另一种解决方案

由于您无法像上面建议的那样更改代码,因此您可以实现以下逻辑:

为包含对象的表创建元表:

local mt = {
    __newindex = function(tab, key, val)
        --if necessary and possible, perform a check, if the value is in fact object of your type
        val.storeTableReference(tab, key) --you'll have to implement this in your objects
        rawset(tab, key, val);
    end
}

local container1 = setmetatable({}, mt)
local container2 = setmetatable({}, mt)

现在,当您将对象插入该表时:

container1.obj1 = obj1
container2.obj1 = obj1

每次__newindex元方法将使用适当的引用调用obj1.storeTableReference。此函数将这些引用存储在(例如)内部表中。

唯一要实现的是对象释放这些引用的方法:

myObj:freeReferences = function()
    for k, v in ipairs(self:tableReferences) do --assuming that's where you store the references
        k[v] = nil
    end
    tableReferences = {} --optional, replaces your reference table with an empty one
end

现在这个解决方案有点笨拙,因为有一些事情你需要谨慎:

  • __newindex只有在首次创建密钥时才会被触发。所以 container1.obj = obj1container1.obj = obj2只会 在第一次分配时触发__newindex。解决方案是 首先将obj键设置为nil,然后设置为obj2
  • 当您在该表中手动(或另一个)将obj设置为nil时 对象),您需要确保对象存储的引用 也被清除了。