|
起因
xlua重启虚拟机时,需要调用Env.Dispose();
但会抛出异常"try to dispose a LuaEnv with C# callback!"- if (!translator.AllDelegateBridgeReleased())
- {
- throw new InvalidOperationException("try to dispose a LuaEnv with C# callback!");
- }
复制代码 这是因为C#端还持有lua的方法没有被释放,所以想要重启虚拟机必须要先把所有的C#委托清空
还需要注意
在同一个方法中释放了delegate的引用,但又立即调用了LuaEnv.Dispose().在Lua端调用了Env.Dispose();
解决方法
UI部分的事件监听是大头,比如- self._btnMapShift.onClick:AddListener(CT.MainCityCtrl.OnBtnMapShift)
- 我们可以使用正则表式式 把这种监听替换成统一的方法里处理,然后在方法,把所有的监听都记录下来,需要重启时统一释放
复制代码 统一处理UI的方法代码- function UIAddListenerIndirect(uiEnlement,eventType,func)
- if uiEnlement[eventType] then
- local callInfo = {
- uiEnlement = uiEnlement,
- eventType = eventType,
- callBackFunc = func
- }
- uiEnlement[eventType]:AddListener(func)
- table.insert(D.UIListenerDelegate,callInfo)
- end
- end
- function UIRemoveListenerIndirect(uiEnlement,eventType,func)
- if uiEnlement[eventType] then
- uiEnlement[eventType]:RemoveListener(func)
- end
- end
- function UIRemoveAllListener()
- for k,v in pairs(D.UIListenerDelegate) do
- local callInfo = v
- --callInfo.uiDelegate:RemoveListener(callInfo.callBackFunc)
- if callInfo.uiEnlement[callInfo.eventType] then
- callInfo.uiEnlement[callInfo.eventType]:RemoveAllListeners()
- callInfo.uiEnlement[callInfo.eventType]:Invoke("",0)
- end
- end
- D.UIListenerDelegate = {}
- end
复制代码 此外还有两个方法查看C#端没被清空的委托 便于我们单独处理
1 xlua提供了一个lua方法(该方法位于XLua/Resources/xlua/util.lua.txt中): print_func_ref_by_csharp()- function print_func_ref_by_csharp()
- local registry = debug.getregistry()
- for k, v in pairs(registry) do
- if type(k) == 'number' and type(v) == 'function' and registry[v] == k then
- local info = debug.getinfo(v)
- print(string.format('%s:%d', info.short_src, info.linedefined))
- end
- end
- end
复制代码 2 修改ObjectTranslator.cs 在注入委托时 记录Lua栈信息,在AllDelegateBridgeReleased时打印出来- Dictionary<int, WeakReference> delegate_bridges = new Dictionary<int, WeakReference>();
- #if UNITY_EDITOR
- // 保存weakReference的hash
- private List<int> bridgeLuaRefs = new List<int>();
- // 保存对应的lua stack info
- private List<string> bridgeLuaRefs2 = new List<string>();
- // 开关
- public bool debugDelegateBridgeRelease = false;
- #endif
- public object CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx)
- {
- WeakReference weakRef = null;
- #if UNITY_EDITOR
- object obj;
- if (this.debugDelegateBridgeRelease)
- {
- string luaStack = this._GetLuaStack(L);
- obj = this._CreateDelegateBridge(L, delegateType, idx, out weakRef);
- if (weakRef != null)
- {
- bridgeLuaRefs.Add(weakRef.GetHashCode());
- bridgeLuaRefs2.Add(luaStack);
- }
- }
- else
- {
- obj = this._CreateDelegateBridge(L, delegateType, idx, out weakRef);
- }
- return obj;
- #else
- return this._CreateDelegateBridge(L, delegateType, idx, out weakRef);
- #endif
- }
- #if UNITY_EDITOR
- private string _GetLuaStack(RealStatePtr L)
- {
- var oldTop = LuaAPI.lua_gettop(L);
- // @see https://www.lua.org/pil/25.2.html
- int debug = LuaAPI.xlua_getglobal(L, "debug");
- // LuaAPI.xlua_pushasciistring(L, "getinfo");
- // LuaAPI.xlua_pgettable(L, -2); // 获取getinfo function
- // LuaAPI.xlua_pushinteger(L, 2); // 传入getinfo()第1个参数
- // LuaAPI.xlua_pushasciistring(L, "Sl"); // 传入getinfo()第2个参数
- // var strIndex = LuaAPI.lua_pcall(L, 2, 1, 0); // 执行函数,此时在栈上的结果是一个table
- // // @see https://www.lua.org/pil/25.1.html
- // LuaAPI.lua_pushstring(L, "source"); // 要获取一个table某个key下的值, 要先传入key的值='source'
- // LuaAPI.xlua_pgettable(L, -2); // 获取table[key]返回的值,此时是一个string
- // var luasource = LuaAPI.lua_tostring(L, -1);
- // LuaAPI.lua_pop(L, 1); // 弹出结果
- // LuaAPI.lua_pushstring(L, "currentline");
- // LuaAPI.xlua_pgettable(L, -2);
- // var currentline = LuaAPI.lua_tostring(L, -1);
- // LuaAPI.lua_pop(L, 1);
- // LuaAPI.lua_settop(L, oldTop);
- // string luaStack = luasource + ":" + currentline;
- // 修改为使用traceback()的信息
- LuaAPI.xlua_pushasciistring(L, "traceback");
- LuaAPI.xlua_pgettable(L, -2);
- var strIndex = LuaAPI.lua_pcall(L, 0, 1, 0);
- string luaStack = LuaAPI.lua_tostring(L, -1);
- LuaAPI.lua_pop(L, 1);
- LuaAPI.lua_settop(L, oldTop);
- return luaStack;
- }
- #endif
- private object _CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx, out WeakReference weakRef)
- {
- LuaAPI.lua_pushvalue(L, idx);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
- if (!LuaAPI.lua_isnil(L, -1))
- {
- int referenced = LuaAPI.xlua_tointeger(L, -1);
- LuaAPI.lua_pop(L, 1);
- weakRef = delegate_bridges[referenced];
- if (weakRef.IsAlive)
- {
- if (delegateType == null)
- {
- return weakRef.Target;
- }
- DelegateBridgeBase exist_bridge = weakRef.Target as DelegateBridgeBase;
- Delegate exist_delegate;
- if (exist_bridge.TryGetDelegate(delegateType, out exist_delegate))
- {
- return exist_delegate;
- }
- else
- {
- exist_delegate = getDelegate(exist_bridge, delegateType);
- exist_bridge.AddDelegate(delegateType, exist_delegate);
- return exist_delegate;
- }
- }
- }
- else
- {
- LuaAPI.lua_pop(L, 1);
- }
- LuaAPI.lua_pushvalue(L, idx);
- int reference = LuaAPI.luaL_ref(L);
- LuaAPI.lua_pushvalue(L, idx);
- LuaAPI.lua_pushnumber(L, reference);
- LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX);
- DelegateBridgeBase bridge;
- try
- {
- #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0
- if (!DelegateBridge.Gen_Flag)
- {
- bridge = Activator.CreateInstance(delegate_birdge_type, new object[] { reference, luaEnv }) as DelegateBridgeBase;
- }
- else
- #endif
- {
- bridge = new DelegateBridge(reference, luaEnv);
- }
- }
- catch (Exception e)
- {
- LuaAPI.lua_pushvalue(L, idx);
- LuaAPI.lua_pushnil(L);
- LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX);
- LuaAPI.lua_pushnil(L);
- LuaAPI.xlua_rawseti(L, LuaIndexes.LUA_REGISTRYINDEX, reference);
- throw e;
- }
- if (delegateType == null)
- {
- weakRef = new WeakReference(bridge);
- delegate_bridges[reference] = weakRef;
- return bridge;
- }
- try
- {
- var ret = getDelegate(bridge, delegateType);
- bridge.AddDelegate(delegateType, ret);
- weakRef = new WeakReference(bridge);
- delegate_bridges[reference] = weakRef;
- return ret;
- }
- catch (Exception e)
- {
- bridge.Dispose();
- throw e;
- }
- }
- public bool AllDelegateBridgeReleased()
- {
- #if UNITY_EDITOR
- var _bridgeLuaRefs = this.bridgeLuaRefs;
- var _bridgeLuaRefs2 = this.bridgeLuaRefs2;
- this.bridgeLuaRefs = new List<int>();
- this.bridgeLuaRefs2 = new List<string>();
- #endif
- bool haveDelegate = false;
- foreach (var kv in delegate_bridges)
- {
- if (kv.Value.IsAlive)
- {
- #if UNITY_EDITOR
- if (this.debugDelegateBridgeRelease)
- {
- var hash = kv.Value.GetHashCode();
- for (int i = 0; i < _bridgeLuaRefs.Count; i++)
- {
- if (_bridgeLuaRefs[i] == hash)
- {
- UnityEngine.Debug.Log("未释放lua引用: " + _bridgeLuaRefs2.Count + " : " + _bridgeLuaRefs2[i]);
- break;
- }
- }
- }
- #endif
- haveDelegate = true;
- }
- }
- return !haveDelegate;
- }
- 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);
- WeakReference weakRef = null;
- delegate_bridges.TryGetValue(reference, out weakRef);
- delegate_bridges.Remove(reference);
- #if UNITY_EDITOR
- if (this.debugDelegateBridgeRelease && weakRef != null)
- {
- var hash = weakRef.GetHashCode();
- for (int i = 0; i < this.bridgeLuaRefs.Count; i++)
- {
- if (bridgeLuaRefs[i] == hash)
- {
- bridgeLuaRefs.RemoveAt(i);
- bridgeLuaRefs2.RemoveAt(i);
- break;
- }
- }
- }
- #endif
- }
- else
- {
- LuaAPI.lua_unref(L, reference);
- }
- }
复制代码 在使用var luaEnv = new LuaEnv()的地方设置luaEnv.translator.debugDelegateBridgeRelease = true, 打开开关,当进行luaEnv.Dispose()时,如果lua侧有未释放的引用,则会打印出lua的对应堆栈信息.
所有UI我们使用统一方法来处理清空
其它部分,我们通过打印日志来单独处理 注意自己写的检查方法最初好用,最后 print_func_ref_by_csharp方法好用 可以两个同时使用
参考链接 https://www.jianshu.com/p/58d20d46560a
https://www.cnblogs.com/ghl_carmack/p/7350530.html |
|