不管是C++中还是在C#中,在都绕不开一个问题:类对象怎么在Lua中使用的问题,还好Lua提供了Userdata以及ligh Userdata结构类型,通过扩展可以处理这方面的问题。现在的很多框架也大致类似的方式进行处理。

在前面的一些笔记<Lua 与 C 交互之UserData(4)>中提到过几个使用宿主类对象中的问题:

  1. 数据内存
  2. 生命周期
  3. 数据操作

那篇文章中给出的案例是《Lua程序设计》中给出的教程,数据放在Lua中,由Lua来管理对象的内存数据,这种方式未必不好,需要优化的是可以在userdata的metatable中增加__gc元方法,去通知宿主语言释放信息,避免宿主语言中对空指针的使用。

Ulua对象管理方式:Ulua采用对象数据在C#层,userdata值记录对象在c#数组中index,并在metatable提供查找对象、访问对象方法的能力以及释放对象时清除c#数据的能力。当然不同的类型Ulua提供的metatable也不尽相同,暂且已class_object来了解这个过程。

支持对象方法访问

在1.03版本以后,ULua中增加了很多其他类型的支持,比如enum、array等,对于每种类型,都会提供对应的metatable提供访问类元素的能力,比如Object_class。当然metatable也可以用作类型的判断。

 private void createIndexingMetaFunction(IntPtr luaState)
{
LuaDLL.lua_pushstring(luaState, "luaNet_indexfunction");
LuaDLL.luaL_dostring(luaState, MetaFunctions.luaIndexFunction);
//LuaDLL.lua_pushstdcallcfunction(luaState,indexFunction);
LuaDLL.lua_rawset(luaState, (int)LuaIndexes.LUA_REGISTRYINDEX);
} //创建物体时
private void pushNewObject(IntPtr luaState, object o, int index, string metatable)
{
...
if (metatable == "luaNet_metatable")
{
// Gets or creates the metatable for the object's type
//string meta = t.AssemblyQualifiedName
//LuaDLL.luaL_getmetatable(luaState, meta);
Type t = o.GetType();
PushMetaTable(luaState, o.GetType()); if (LuaDLL.lua_isnil(luaState, -1))
{
string meta = t.AssemblyQualifiedName;
Debugger.LogWarning("Create not wrap ulua type:" + meta);
LuaDLL.lua_settop(luaState, -2);
LuaDLL.luaL_newmetatable(luaState, meta);
LuaDLL.lua_pushstring(luaState, "cache");
LuaDLL.lua_newtable(luaState);
LuaDLL.lua_rawset(luaState, -3);
LuaDLL.lua_pushlightuserdata(luaState, LuaDLL.luanet_gettag());
LuaDLL.lua_pushnumber(luaState, 1);
LuaDLL.lua_rawset(luaState, -3);
LuaDLL.lua_pushstring(luaState, "__index");
LuaDLL.lua_pushstring(luaState, "luaNet_indexfunction");
LuaDLL.lua_rawget(luaState, (int)LuaIndexes.LUA_REGISTRYINDEX);
LuaDLL.lua_rawset(luaState, -3);
LuaDLL.lua_pushstring(luaState, "__gc");
LuaDLL.lua_pushstdcallcfunction(luaState, metaFunctions.gcFunction);
LuaDLL.lua_rawset(luaState, -3);
LuaDLL.lua_pushstring(luaState, "__tostring");
LuaDLL.lua_pushstdcallcfunction(luaState, metaFunctions.toStringFunction);
LuaDLL.lua_rawset(luaState, -3);
LuaDLL.lua_pushstring(luaState, "__newindex");
LuaDLL.lua_pushstdcallcfunction(luaState, metaFunctions.newindexFunction);
LuaDLL.lua_rawset(luaState, -3);
}
}
else
{
LuaDLL.luaL_getmetatable(luaState, metatable);
}
...
}

在添加新对象的时候,会创建的Metatable,并设置元方法。通过 _ _ index实现对类对象的访问,当然这里是通过反射机制来实现的。

通过Wraper全局注册的方式并不需要这里的_ _index的访问了。Ulua经过几个版本的更新,代码相当的混乱了。到 toLuaC#中其实__Index已经删除。

对象管理

对象列表

C#维护一个weak table,以数组index为key记录对象userdata数据。

 private void createLuaObjectList(IntPtr luaState)
{
LuaDLL.lua_pushstring(luaState, "luaNet_objects");
LuaDLL.lua_newtable(luaState);
LuaDLL.lua_pushvalue(luaState, -1);
weakTableRef = LuaDLL.luaL_ref(luaState, LuaIndexes.LUA_REGISTRYINDEX);
LuaDLL.lua_pushvalue(luaState, -1);
LuaDLL.lua_setmetatable(luaState, -2);
LuaDLL.lua_pushstring(luaState, "__mode");
LuaDLL.lua_pushstring(luaState, "v");
LuaDLL.lua_settable(luaState, -3);
//LuaDLL.lua_setmetatable(luaState,-2);
LuaDLL.lua_settable(luaState, (int)LuaIndexes.LUA_REGISTRYINDEX);
}

增加对象

在添加物体时 会产生一个index作为该object唯一的参数保存在一张C#列表中,并将这个Indx和userdata数据放在一个weaktable(weakTableRef为LuaIndexes.LUA_REGISTRYINDEX表中索引)。

private void pushNewObject(IntPtr luaState, object o, int index, string metatable)
{
LuaDLL.lua_getref(luaState, weakTableRef);
LuaDLL.luanet_newudata(luaState, index);
...
//创建metatable,并放在入栈
...
LuaDLL.lua_setmetatable(luaState, -2);
LuaDLL.lua_pushvalue(luaState, -1);
LuaDLL.lua_rawseti(luaState, -3, index);
LuaDLL.lua_remove(luaState, -2);
...
}
//返回新的Index
int addObject(object obj)
{
// New object: inserts it in the list
int index = nextObj++;
objects[index] = obj;
objectsBackMap[obj] = index; return index;
}

获取对象

顺便提一下UserData中存储的是index值,了解下luanet_newudata接口的实现就可以明白。

LUALIB_API void luanet_newudata(lua_State *L,int val)
{
int* pointer=(int*)lua_newuserdata(L,sizeof(int));
*pointer=val;
}

那怎么获取对象呢?既然知道userdata中存储的是index的数据,那么就可以在获取userdata之后,直接在C#中查表获得对象。

LUALIB_API int luanet_rawnetobj(lua_State *L,int index)
{
int *udata = lua_touserdata(L,index);
if(udata!=NULL) return *udata;
return -1;
} //C#中接口
internal object getRawNetObject(IntPtr luaState, int index)
{
int udata = LuaDLL.luanet_rawnetobj(luaState, index);
object obj = null;
objects.TryGetValue(udata, out obj);
return obj;
}

删除对象

既然对象保存在C#层,那Lua中如果释放了该对象,C#层如何释放呢?还是通过metatable中元方法,[Lua5.1中userdata支持_ GC方法][3],对象被Lua回收期释放的时候调用这个方法。C#层中将对象移除。

internal void collectObject(int udata)
{
object o;
bool found = objects.TryGetValue(udata, out o);
// The other variant of collectObject might have gotten here first, in that case we will silently ignore the missing entry
if (found)
{
objects.Remove(udata);
if (o != null && !o.GetType().IsValueType)
{
objectsBackMap.Remove(o);
}
}
}

对于增加新物体时,如果发现这个对象在C#缓冲列表中存在,但是在Lua中不存在,那就可以删除了。这里有使用到weak table,也就是如果userdata不在被引用,就会被Lua垃圾收集器回收。

 public void pushObject(IntPtr luaState, object o, string metatable)
{
if (o == null)
{
LuaDLL.lua_pushnil(luaState);
return;
} int index = -1;
// Object already in the list of Lua objects? Push the stored reference.
bool beValueType = o.GetType().IsValueType;
if (!beValueType && objectsBackMap.TryGetValue(o, out index))
{
if (LuaDLL.tolua_pushudata(luaState, weakTableRef, index))
{
return;
} // Note: starting with lua5.1 the garbage collector may remove weak reference items (such as our luaNet_objects values) when the initial GC sweepoccurs, but the actual call of the __gc finalizer for that object may not happen until a little while later. During that window we might call this routine and find the element missing from luaNet_objects, but collectObject() has not yet been called. In that case, we go ahead and call collect object here
// did we find a non nil object in our table? if not, we need to call collect object Remove from both our tables and fall out to get a new ID
collectObject(o, index);
}
index = addObject(o, beValueType);
pushNewObject(luaState, o, index, metatable);
}

存在的问题

  1. 如果Lua中一直持有对象引用,就不会释放这个对象,造成无法回收
  2. 物体不使用的时候直接Destroy掉,会增加GC消耗,增加pool的会更合理一点

    在后续版本中对这些情况也做了对应的优化。

[2]:http://www.cnblogs.com/zsb517/p/6423342.html “Lua 基础之Weak Table(5)”

[3]:http://www.cnblogs.com/zsb517/p/6423472.html "Lua基础之MetaTable(6)"

Ulua对象管理方式的更多相关文章

  1. Kubernetes 对象管理的三种方式

    Kubernetes 中文文档 1. Kubernetes 对象管理的三种方式对比 Kubernetes 中的对象管理方式,根据对象配置信息的位置不同可以分为两大类: 命令式:对象的参数通过命令指定 ...

  2. 24小时学通Linux内核之内存管理方式

    昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今天将会讲诉Linux如何追踪和管理用户空间进程的可用内 ...

  3. [Effective C++ --013]以对象管理资源

    这一节基本讲述的是将资源放进管理对象,防止忘记释放资源. 1.一般New和Delete使用场景 void fun() { SimpleClass* pSimpleClass1 = new Simple ...

  4. ORACLE表空间管理方式segment和extent

    A permanent tablespace contains persistent schema objects. Objects in permanent tablespaces are stor ...

  5. 十天学Linux内核之第三天---内存管理方式

    原文:十天学Linux内核之第三天---内存管理方式 昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今 ...

  6. ObjC如何通过runtime修改Ivar的内存管理方式

    ObjC如何通过runtime修改Ivar的内存管理方式 为什么要这么做? 在iOS 9之前,UITableView(或者更确切的说是 UIScrollView)有一个众所周知的问题: propert ...

  7. Java内存管理及对Java对象管理

    Java内存管理及对Java对象管理 1Java内存管理 1.1Java中的堆和栈 通常来说,人们会将Java内存氛围栈内存(Stack)和堆内存(Heap). 栈内存用来保存基本类型的变量和对象的引 ...

  8. Oracle表空间的管理方式

    解释说明:表空间是一个逻辑概念:=> oracle 逻辑概念段区块管理方式: number one => tablespace number two=> segments Oracl ...

  9. Mybatis事务(一)事务管理方式

    Mybatis管理事务是分为两种方式: (1)使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交 (2)使用MANAGED的事务管理机制,这种机制mybat ...

随机推荐

  1. CSS 基础:HTML 标记与文档结构(1)<思维导图>

    这段时间利用一下间隙时间学习了CSS的基础知识,主要目的是加深对CSS的理解,虽然个人主要工作基本都是后台开发,但是个人觉得系统学习一下CSS的基础还是很有必要的.下面我学习CSS时做的思维导图(全屏 ...

  2. python机器学习笔记 ID3决策树算法实战

    前面学习了决策树的算法原理,这里继续对代码进行深入学习,并掌握ID3的算法实践过程. ID3算法是一种贪心算法,用来构造决策树,ID3算法起源于概念学习系统(CLS),以信息熵的下降速度为选取测试属性 ...

  3. 使用Asp.Net Core MVC 开发项目实践[第二篇:EF Core]

    在项目中使用EF Core还是比较容易的,在这里我们使用的版本是EF Core 2.2. 1.使用nuget获取EF Core包 这个示例项目使用的是SQLSERVER,所以还需要下载Microsof ...

  4. JavaWeb总结(国税)

    一.JavaWeb示例 1.1.Servlet Hello World&IDEA创建第一个Servlet 新建项目 选择maven 组织名与项目名 位置,完成 允许自动导入包 添加框架支持,变 ...

  5. (转)Visual Studio 2013新功能预览:增代码的透明度和可追溯性

    微软打破了Visual Studio两年升级一次的传统,Visual Studio 2012发布还不足一年,微软就计划发布了Visual Studio 2013了.在今天的TechEd大会上,微软宣布 ...

  6. SqlServer存储过程,学习

    存储过程:存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数) ...

  7. 使用 New Relic 监控接口服务性能 (APM)

    偶然看到贴子在使用[Rails API] 使用这个APM监控,今天试了下.NET IIS环境下,配置一路NEXT即可. 主要指标 服务响应时间 Segment SQL执行时间 安全问题 1.走HTTP ...

  8. (4)Jquery1.8.3快速入门_基本选择器

    一.Jquery选择器: 基本选择器: 1.id                           #id      根据元素的id获取的唯一元素. 2.class                  ...

  9. java-两个整数变量的交换-不需要定义第三方变量

    代码如下: class Example { public static void main(String[] args) { /* * 位异或运算符的特点 * ^的特点:一个数据对另一个数据位异或两次 ...

  10. Modular Inverse(zoj3609+欧几里德)

    Modular Inverse Time Limit: 2 Seconds      Memory Limit: 65536 KB The modular modular multiplicative ...