Memory 在Lua中通过引用删除变量

Memory 在Lua中通过引用删除变量,memory,lua,reference,lua-table,Memory,Lua,Reference,Lua Table,我在几张桌子里找到了几件东西。 多个功能更改对象并将其移交给其他功能 假设我的桌子是这样的: objectTable = {obj1, obj2, obj3} otherobjTable = {objA, objB, objC, objD} 假设这些都是在main.lua中初始化的 现在,当跟踪obj1时,它被一个函数改变,这个函数改变了它,并给出了对另一个函数的引用,这个函数再次改变了它。 一个步骤可能看起来像: function() if something then func(ob

我在几张桌子里找到了几件东西。 多个功能更改对象并将其移交给其他功能

假设我的桌子是这样的:

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
现在的问题是,在接收到\u Object=nil后,参考点为nil,但对象仍然存在。如何确保删除它?

在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
现在这个解决方案有点笨拙,因为有几件事你需要小心:

  • \uuuu newindex
    仅在首次创建密钥时触发get。所以
    container1.obj=obj1
    container1.obj=obj2
    只会 在第一次赋值时触发
    \uuu newindex
    。解决办法是 首先将
    obj
    键设置为nil,然后设置为
    obj2
  • 当您在该表中将
    obj
    手动设置为nil(或另一个 对象),您需要确保对象存储的引用 也会被清除

所以无法清除内存位置?由于串(对象),我得到了记忆位置。我破解了另一个解决方案,通过迭代对象可能位于的所有表。这是可行的,但正如我所说,这是最痛苦和最愚蠢的方式。我会在以后的项目中记住你的策略,但如果可能的话,这次我更希望有一个解决方案,我不必对10000行代码进行分析。由于最多3个表中的对象少于100个,所以迭代不会花费太长时间。@Sempie在不了解代码的情况下,很难提出任何建议。另一种解决方案是让对象(是表还是C userdata?)存储对引用它们的所有表/键的引用。然后在对象中实现一个
delete
方法,该方法迭代这些表,并将适当的键设置为
nil
。通过在表插入中使用
\uuu newindex
元方法在插入时存储这些引用,这可以在表插入中半自动化。也许
weak
表也可以是一个解决方案-不知道您的项目就很难判断。这听起来很棒。在我的例子中,这可能会更容易实现,因为更改每个参数。如果你愿意在你的答案中加入这个想法,我将把它标记为解决方案。谢谢。很好的解释,但是你使用了流行语“按参考传递”和“按价值传递”。不要引起混乱,但这不是计算机科学和C++语言如何使用术语。这是一个漫长的讨论。