展会信息港展会大全

cocos2d-x LUA 2.x里tolua++的一个隐患
来源:互联网   发布日期:2015-09-28 11:08:26   浏览:2367次  

导读: 如果是在cocos2d-x里才第一次使用lua的人,里面的extern.lua会带来很大的帮助,因为它帮lua这种勉强算是基于对象的语言实现了继承的功能。 同时,cocos2d-x里集成了to...

如果是在cocos2d-x里才第一次使用lua的人,里面的extern.lua会带来很大的帮助,因为它帮lua这种勉强算是基于对象的语言实现了继承的功能。

同时,cocos2d-x里集成了tolua++,使自己编写的c++类可以通过配置,轻松地生成lua的绑定代码。

但不一定所有人都会把tolua++研究透,也不一定会所有人都把tolua++的使用方法搞明白。

当项目刚开始的时候,我们就发现了tolua++的释放机制其实是不算完善的。例如以下代码:

tolua_pushusertype(tolua_S,tolua_obj,"CCSize");

tolua_register_gc(tolua_S,lua_gettop(tolua_S));

虽然表面上看来是注册了回收机制,但实际上,并不会立刻回收,而是到达一定峰值的时候才会回收,这样会导致游戏在某一个时刻出现卡顿的情况。

以后问题先不说,因为解决的手段是根据实际场景的。这段时间我们又发现了一个更严重的问题,由于我们仿照了basic.lua,另外建立了一个baseGameLib.lua,用来将自己写的c++代码绑定到lua,随着项目的推进,我们自己新写的c++代码越来越多,逻辑也越来越复杂,甚至我们还扩展了extern.lua里的class机制,保存了继承树,加入了类似java里isInstance这样的语法。终于隐藏在几种因素的综合下爆发了,游戏在运行中,竟然会出现原来A继承B,但在运行中却突然变成B继承A的问题。查了一个多星期,终于发现了问题所在。

在basic.lua文件的最上面,就有以下的代码:

local CCObjectTypes = {

"CCObject",

"CCAction",

"CCImage",

"CCFiniteTimeAction",

.......

"

}

-- register CCObject types

for i = 1, #CCObjectTypes do

_push_functions[CCObjectTypes[i]] = "toluafix_pushusertype_ccobject"

end

这段代码的作用是指定了有那些c++的类是继承CCObject的,然后只要在这里配置了,在tolua++生成的时候就会使用

toluafix_pushusertype_ccobject来返回给lua,否则,如果不在这里配置的所有c++类,一律使用

toluafix_pushusertype返回。

以下是toluafix_pushusertype的代码:

void tolua_pushusertype_internal (lua_State* L, void* value, const char* type, int addToRoot)

{

if (value == NULL)

lua_pushnil(L);

else

{

luaL_getmetatable(L, type);/* stack: mt */

if (lua_isnil(L, -1)) { /* NOT FOUND metatable */

lua_pop(L, 1);

return;

}

lua_pushstring(L,"tolua_ubox");

lua_rawget(L,-2);/* stack: mt ubox */

if (lua_isnil(L, -1)) {

lua_pop(L, 1);

lua_pushstring(L, "tolua_ubox");

lua_rawget(L, LUA_REGISTRYINDEX);

};

lua_pushlightuserdata(L,value);/* stack: mt ubox key */

lua_rawget(L,-2);/* stack: mt ubox ubox[value] */

if (lua_isnil(L,-1))

{

lua_pop(L,1);/* stack: mt ubox */

lua_pushlightuserdata(L,value);

*(void**)lua_newuserdata(L,sizeof(void *)) = value;/* stack: mt ubox value newud */

lua_pushvalue(L,-1);/* stack: mt ubox value newud newud */

lua_insert(L,-4);/* stack: mt newud ubox value newud */

lua_rawset(L,-3);/* ubox[value] = newud, stack: mt newud ubox */

lua_pop(L,1);/* stack: mt newud */

/*luaL_getmetatable(L,type);*/

lua_pushvalue(L, -2);/* stack: mt newud mt */

lua_setmetatable(L,-2);/* update mt, stack: mt newud */

#ifdef LUA_VERSION_NUM

lua_pushvalue(L, TOLUA_NOPEER);/* stack: mt newud peer */

lua_setfenv(L, -2);/* stack: mt newud */

#endif

}

else

{

/* check the need of updating the metatable to a more specialized class */

lua_insert(L,-2);/* stack: mt ubox[u] ubox */

lua_pop(L,1);/* stack: mt ubox[u] */

lua_pushstring(L,"tolua_super");

lua_rawget(L,LUA_REGISTRYINDEX);/* stack: mt ubox[u] super */

lua_getmetatable(L,-2);/* stack: mt ubox[u] super mt */

lua_rawget(L,-2);/* stack: mt ubox[u] super super[mt] */

if (lua_istable(L,-1))

{

lua_pushstring(L,type);/* stack: mt ubox[u] super super[mt] type */

lua_rawget(L,-2);/* stack: mt ubox[u] super super[mt] flag */

if (lua_toboolean(L,-1) == 1)/* if true */

{

lua_pop(L,3);/* mt ubox[u]*/

lua_remove(L, -2);

return;

}

}

/* type represents a more specilized type */

/*luaL_getmetatable(L,type);// stack: mt ubox[u] super super[mt] flag mt */

lua_pushvalue(L, -5);/* stack: mt ubox[u] super super[mt] flag mt */

lua_setmetatable(L,-5);/* stack: mt ubox[u] super super[mt] flag */

lua_pop(L,3);/* stack: mt ubox[u] */

}

lua_remove(L, -2);/* stack: ubox[u]*/

if (0 != addToRoot)

{

lua_pushvalue(L, -1);

tolua_add_value_to_root(L, value);

}

}

}

以下是toluafix_pushusertype_ccobject方法的代码:

TOLUA_API int toluafix_pushusertype_ccobject(lua_State* L,

int refid,

int* p_refid,

void* ptr,

const char* type)

{

if (ptr == NULL || p_refid == NULL)

{

lua_pushnil(L);

return -1;

}

if (*p_refid == 0)

{

*p_refid = refid;

lua_pushstring(L, TOLUA_REFID_PTR_MAPPING);

lua_rawget(L, LUA_REGISTRYINDEX);/* stack: refid_ptr */

lua_pushinteger(L, refid);/* stack: refid_ptr refid */

lua_pushlightuserdata(L, ptr);/* stack: refid_ptr refid ptr */

lua_rawset(L, -3);/* refid_ptr[refid] = ptr, stack: refid_ptr */

lua_pop(L, 1);/* stack: - */

lua_pushstring(L, TOLUA_REFID_TYPE_MAPPING);

lua_rawget(L, LUA_REGISTRYINDEX);/* stack: refid_type */

lua_pushinteger(L, refid);/* stack: refid_type refid */

lua_pushstring(L, type);/* stack: refid_type refid type */

lua_rawset(L, -3);/* refid_type[refid] = type, stack: refid_type */

lua_pop(L, 1);/* stack: - */

//printf("[LUA] push CCObject OK - refid: %d, ptr: %x, type: %s\n", *p_refid, (int)ptr, type);

}

tolua_pushusertype_and_addtoroot(L, ptr, type);

return 0;

}

通俗点的说,

toluafix_pushusertype是有隐患的,就好像我刚刚所说的,在各种复杂条件配合下,例如在某一段代码开了定时器,定时器逻辑里的对象又有复杂的继承树,并又使用了isInstance这种功能,或者还有其它一些我也想不明白的条件,就算明明在c++里创建了一个对象,并绑定到lua的时候,if (lua_isnil(L,-1))这段代码会返回false,直接导致报错。

所以,cocos2d-x的作者们很聪明地发明了toluafix_pushusertype_ccobject,只要我们完全按照basic.lua一样,将自己新建的继承CCObject的类,都配置在basic.lua里,就绝对不会出现我上面所说的问题。

赞助本站

人工智能实验室
AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港