XLua框架搭建——XLua API与try to dispose a LuaEnv with C# callback

在lua的设计中,有些是c语言与lua相互调用的方法,作为使用c#的unity,xlua也是用c#来进行编写,所以我们现在要使用c#代码来做这些事,xlua提供了相对应的方法,这些方法在LuaAPI类里。

LuaAPI.lua_gettop();
LuaAPI.xlua_getglobal();
LuaAPI.xlua_pgettable();
LuaAPI.lua_pushvalue();
LuaAPI.lua_pushnumber();
LuaAPI.lua_pcall();
LuaAPI.lua_pop();

上面列举了几个常见的函数,一般都有对应的函数,通过这些函数我们可以知道这些函数的作用,一般根据函数名来说也可以知道这个函数的作用了。
举个例子:LuaAPI.lua_gettop()是取栈顶元素,对应的方法是lua_gettop,而LuaAPI.xlua_getglobal()对应的函数是lua_getglobal

接下来在说下游戏中常见的错误“try to dispose a LuaEnv with C# callback”,关于这个错误可以在faq里找到

调用LuaEnv.Dispose时,报“try to dispose a LuaEnv with C# callback!”错是什么原因?
这是由于C#还存在指向lua虚拟机里头某个函数的delegate,为了防止业务在虚拟机释放后调用这些无效(因为其引用的lua函数所在虚拟机都释放了)delegate导致的异常甚至崩溃,做了这个检查。
怎么解决?释放这些delegate即可,所谓释放,在C#中,就是没有引用:
你是在C#通过LuaTable.Get获取并保存到对象成员,赋值该成员为null;
你是在lua那把lua函数注册到一些事件事件回调,反注册这些回调;
如果你是通过xlua.hotfix(class, method, func)注入到C#,则通过xlua.hotfix(class, method, nil)删除;
要注意以上操作在Dispose之前完成。

原因就是引用未释放,但是虽然知道引用未释放,我们依然不知道到底是哪个脚本哪行代码引起的,一般来说我们可以通过出错时机来模糊定位出错的脚本,毕竟执行到这里时在停掉游戏就报了这个错,那问题肯定出现在刚加载的模块,但是如何从该模块众多代码中去定位到底是哪行代码导致的呢?我们就需要利用上面的那些函数了。

c#代码对lua进行引用时需要创建一个delegate,对应的代码在ObjectTranslator.CreateDelegateBridge函数,所以我们可以得到所有的lua引用,但是这些引用信息比较简单,我们能拿到的信息有RealStatePtr L, Type delegateType,int reference,如何利用这些信息呢?

分析对lua引用的方法,一个是通过table.Get比如LuaBehaviour设计中获取Awake等方法时来获取lua引用;一种是在lua中调用c#代码将函数作为参数传递从而获得引用。

知道了这两种方法,我们需要分开处理,在刚才委托创建的地方,首先我们可以获取当前c#调用堆栈信息,看栈中是否存在“XLua.LuaTable:Get”,如果有,那么是通过第一种方式来进行处理的,我们分析下调用堆栈字符串,然后根据规律手动截取它之前的堆栈信息就可以知道在dispose时未释放的代码是在这一行。如果没有这行代码,那么就是通过第二种方式取得的引用,对于第二种,我们需要得到lua端的调用堆栈,在lua代码中,我们可以通过debug.traceback或是debug.getinfo函数来获取调用堆栈,可以在c#中如何去获取这个堆栈信息呢?

在lua和c#相互调用时,会有一个交互的栈,我们就利用这个栈和前面所列举的这些函数来达到我们的目的。

int debug = LuaAPI.xlua_getglobal(L, "debug");
LuaAPI.xlua_pushasciistring(L, "getinfo");
int info = LuaAPI.xlua_pgettable(L, -2);

原理还是通过debug.getinfo来获取对应的调用堆栈,解释下上面的代码,首先获取lua中的table:debug,然后将函数名getinfo压入栈中,通过函数LuaAPI.xlua_pgettable获取到debug.getinfo,函数并将其压入栈中,然后我们可以压入参数LuaAPI.xlua_pushasciistring(L, “nSl”)然后调用LuaAPI.lua_pcall来获取堆栈信息,最后将栈顶重置回原来的位置LuaAPI.lua_settop(L, oldTop);
通过这些操作可以拿到产生引用的lua代码文件名,行数信息,进而进行精准定位。

到这一步基本上就做的差不多了,上面的这些是获取了所有的委托,最后在ObjectTranslator.AllDelegateBridgeReleased里检查是哪个没释放的引用,根据kv.Value.Target的luaReference来查询刚才保存的所有委托信息,进行输出就是我们想要的信息了。

QQ交流群:517539056,欢迎大家加入一起交流

你可能感兴趣的:(Lua学习,xlua,框架,api,堆栈,错误)