好的,所以我有一个使用SWIG暴露给Lua的C ++类。该脚本创建了该对象,但是一个管理器类也有一个指向该对象的指针,因此无论出于何种原因,它都可以在C ++(或其他脚本)中进行修改。
问题是,当脚本完成对象被释放时,如何控制垃圾收集器收集的内容而不必实现gc元方法?
以下是一个例子:
--Script that creates the object
someObject = Utils.Object("Obj name");
现在,Object已经向管理器注册了,因此应用程序的其余部分(以及其他脚本)可以访问它。
--Another script
obj = ObjManager:GetObject(0);
显然不是一个非常现实的例子,但希望它能说明我的问题。有没有办法在没有C ++的gc元方法的情况下否决垃圾收集器?
只是为了澄清管理器是在C ++中,而Utils是容纳暴露类的模块名称。该对象也在其构造函数中将自身注册到管理器。
提前致谢。
答案 0 :(得分:2)
Lua的GC只知道Lua中的引用,这是一个合理的实现约束。这意味着对象的生命周期受Lua的控制。如果通过执行一个脚本或函数创建的对象需要可用于以后的脚本或函数,则必须在Lua状态中保留对它的引用,以便GC 知道它仍在使用中。否则,它与垃圾无法区分,并且可能随时被丢弃。
这是Lua注册表的目的之一。通过将它放在注册表中,C端可以轻松地保存对任何Lua对象的引用。密钥可以是C库已知的一些唯一值(转换为轻用户数据的static
变量的地址通常是一个很好的选择,因为它不能与来自任何其他库的任何密钥冲突)。或者,函数调用luaL_ref(L, LUA_REGISTRYINDEX)
将把项放在注册表中的堆栈顶部并返回一个唯一的整数键。这适用于存储脚本提供的回调函数,其方式既保护函数不受GC影响,又允许将“指针”(整数键)存储在C结构中,以便可以检索和调用它。后面。
请注意,luaL_ref()
可用于管理任何表中的引用,因此使用模块专用的表而不是全局注册表表可能是有意义的。在这种情况下,表ObjManager
本身可能是一个很好的候选者。
答案 1 :(得分:0)
是;让Utils.Object将对象填充到私有表中。然后它永远将被收集,但你可以玩游戏(代码未经测试):
do
local retained = { } -- table forces objects to be retained
local old_util_object = Util.Object
Util.Object = function(...)
local obj = old_util_object(...)
retained[obj] = true
return obj
end
Util.Free = function(obj)
assert(retained[obj])
retained[obj] = nil -- now obj can be garbage-collected
end
end
如果要在C ++端解决相同的问题,请让您的C ++代码分配一个私有表并将其放在Lua注册表中。然后你可以玩相同的插入/删除游戏,只使用C API而不是Lua源。假设您对Lua的C API有任何熟悉,那就很简单了。如果您以前没有使用过C API,那么将永远不会有更好的时间开始学习。