当为TestXLua.func赋值Lua function时,会触发func变量的set包裹方法_s_set_func,_s_set_func内部会获取一个委托设置给func变量。这个委托绑定的是DelegateBridge对象的以"__Gen_Delegate_Imp"开头的生成方法,DelegateBridge对象同时保存着Lua function的索引
// CS测试代码 TestXLua.func("test", false, 3);当调用TestXLua.func时,相当于调用以"__Gen_Delegate_Imp"开头的生成方法,这个生成方法负责参数压栈,并通过保存的索引获取到Lua function,然后使用lua_pcall完成Lua function的调用
GCC#和Lua都是有自动垃圾回收机制的,并且相互是无感知的。如果传递到C#的Lua对象被Lua自动回收掉了,而C#这边仍毫不知情继续使用,则必然会导致无法预知的错误。所以基本原则是传递到C#的Lua对象,Lua不能自动回收,只能C#在确定不再使用后通知Lua进行回收
为了保证Lua不会自动回收对象,所有传递给C#的对象都会被Lua注册表引用。比如前面创建LuaTable或DelegateBridge时 都有调用LuaAPI.luaL_ref将对象添加到注册表中
C#这边为对应的Lua对象定义了LuaBase基类,LuaTable或DelegateBridge均派生于LuaBase,这个类实现了IDisposable接口,并且在析构函数中会调用Dispose
// LuaBase.cs public virtual void Dispose(bool disposeManagedResources) { if (!disposed) { if (luaReference != 0) { #if THREAD_SAFE || HOTFIX_ENABLE lock (luaEnv.luaEnvLock) { #endif bool is_delegate = this is DelegateBridgeBase; if (disposeManagedResources) { luaEnv.translator.ReleaseLuaBase(luaEnv.L, luaReference, is_delegate); // 释放Lua对象 } else //will dispse by LuaEnv.GC { luaEnv.equeueGCAction(new LuaEnv.GCAction { Reference = luaReference, IsDelegate = is_delegate }); // 加入GC队列 } #if THREAD_SAFE || HOTFIX_ENABLE } #endif } disposed = true; } }当disposeManagedResources为true时,直接调用ReleaseLuaBase释放Lua对象
// ObjectTranslator.cs public void ReleaseLuaBase(RealStatePtr L, int reference, bool is_delegate) { if(is_delegate) { LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, reference); if (LuaAPI.lua_isnil(L, -1)) { LuaAPI.lua_pop(L, 1); } else { LuaAPI.lua_pushvalue(L, -1); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); if (LuaAPI.lua_type(L, -1) == LuaTypes.LUA_TNUMBER && LuaAPI.xlua_tointeger(L, -1) == reference) // { //UnityEngine.Debug.LogWarning("release delegate ref = " + luaReference); LuaAPI.lua_pop(L, 1);// pop LUA_REGISTRYINDEX[func] LuaAPI.lua_pushnil(L); LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); // LUA_REGISTRYINDEX[func] = nil } else //another Delegate ref the function before the GC tick { LuaAPI.lua_pop(L, 2); // pop LUA_REGISTRYINDEX[func] & func } } LuaAPI.lua_unref(L, reference); delegate_bridges.Remove(reference); } else { LuaAPI.lua_unref(L, reference); } }ReleaseLuaBase的主要任务是将Lua对象从Lua注册表中移除,这样Lua GC时发现该对象不再被引用,就可以进行回收了
// LuaEnv.cs public void Tick() { #if THREAD_SAFE || HOTFIX_ENABLE lock (luaEnvLock) { #endif var _L = L; lock (refQueue) { while (refQueue.Count > 0) // 遍历GC队列 { GCAction gca = refQueue.Dequeue(); translator.ReleaseLuaBase(_L, gca.Reference, gca.IsDelegate); } } #if !XLUA_GENERAL last_check_point = translator.objects.Check(last_check_point, max_check_per_tick, object_valid_checker, translator.reverseMap); #endif #if THREAD_SAFE || HOTFIX_ENABLE } #endif }当disposeManagedResources为false时,会将其加入GC队列。当主动释放Lua环境时,会遍历GC队列,再逐一调用ReleaseLuaBase进行释放
参考添加了中文注释的xLua源码
深入xLua实现原理之Lua如何调用C#