Sem*_*pie 3 memory lua reference lua-table
我在几个表中得到了几个对象。多个功能更改对象并将其移交给其他功能。
假设我的表是这样的:
objectTable = {obj1, obj2, obj3}
otherobjTable = {objA, objB, objC, objD}
Run Code Online (Sandbox Code Playgroud)
假设这些是在 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
Run Code Online (Sandbox Code Playgroud)
现在的问题是,在received_Object = nil 之后,引用指向 nil 但对象仍然存在。我怎样才能确定删除它?
在 Lua 中,某些类型(如表)总是通过引用传递,而其他类型(如数字)总是通过值传递。
此外,Lua 是一种内存由垃圾收集器管理的语言。垃圾收集器删除一个对象(例如一个表),当没有更多的引用(让我们称它们为锚点)时。
现在这个代码:
local t = {}
local t1 = {t}
someFunc(t)
Run Code Online (Sandbox Code Playgroud)
为该表创建三个锚点。什么时候someFunc将另一个该表作为参数传递给另一个函数时,将创建第四个锚点(以该函数的局部变量/参数的形式)。
为了让垃圾收集器清除第一个表,所有这些引用都必须消失(通过分配 nil或超出范围)。
重要的是要了解,当您分配nil给本地t,并不意味着该表将被删除。更不用说对该表的所有引用都将无效。这意味着你只是释放了这个锚点,在这一点上它只是四个锚点之一。
可能的解决方案
一种可能的解决方案是传递包含对象的表以及存储对象的索引/键:
function func(myTable, myKey)
...
end
Run Code Online (Sandbox Code Playgroud)
现在,如果在这个函数中你这样做:
myTable[myKey] = nil
Run Code Online (Sandbox Code Playgroud)
(并且没有其他锚点被创建),键下的对象将不再有指向它的引用,并将被标记为垃圾收集器下一次清除。当然,callAnotherFunction也必须以相同的方式进行修改:
callAnotherFunction(myTable, myKey)
...
end
Run Code Online (Sandbox Code Playgroud)
如果您在这些函数中对该对象执行许多操作,您可以将其缓存到一个局部变量中以避免多次表查找。这没关系,因为当函数完成时,锚点将与局部变量一起被清除:
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.
Run Code Online (Sandbox Code Playgroud)
另一种解决方案
由于您无法像上面建议的那样更改代码,因此您可以实现以下逻辑:
为包含对象的表创建元表:
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)
Run Code Online (Sandbox Code Playgroud)
现在,当您将对象插入该表时:
container1.obj1 = obj1
container2.obj1 = obj1
Run Code Online (Sandbox Code Playgroud)
每次 __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
Run Code Online (Sandbox Code Playgroud)
现在这个解决方案有点笨拙,因为您需要注意以下几点:
__newindex只有在第一次创建密钥时才会触发。所以
container1.obj = obj1并且container1.obj = obj2只会__newindex在第一次分配时触发。解决方案是首先将obj键设置为 nil 然后设置为obj2。obj在该表中手动(或另一个对象)设置为 nil 时,您需要确保对象存储的引用也被清除。