我在几个表中有几个对象。 多个功能改变并将对象切换到其他功能。
让我们说我的表是这样的:
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 之后,参考点为零,但对象仍然存在。如何确保将其删除?
答案 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 = obj1
和container1.obj = obj2
只会
在第一次分配时触发__newindex
。解决方案是
首先将obj
键设置为nil,然后设置为obj2
。obj
设置为nil时
对象),您需要确保对象存储的引用
也被清除了。