cocos2d-x LUA 2.x里tolua++的一个隐患

来源:互联网 发布:mac del rio 编辑:程序博客网 时间:2024/05/17 07:10

如果是在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 typesfor 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<value> */        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里,就绝对不会出现我上面所说的问题。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 脚踩垃圾桶坏了怎么办 连衣裙特别容易起褶怎么办 施肥过度烧根了怎么办 农作物施用尿素发生肥害怎么办? 水稻尿素施多了怎么办 花施肥施多了怎么办 玉米被化肥烧了怎么办 撒施复合肥没有充分融化怎么办 绿箩化肥施多了怎么办 辣椒化肥施多了怎么办? 化肥施多了烧苗怎么办 绿植施肥施多了怎么办 盆栽肥料放多了怎么办 三环复合肥怎么办啊 母猪下崽后不吃食怎么办 猪自配料料槽不下怎么办 美甲边缘起翘怎么办 猪粪流到鱼塘里鱼死了怎么办啊 在基本农田建有机肥厂怎么办 有机肥厂的环评怎么办 织玻璃纤维网布环评怎么办 吃了受潮的奶粉怎么办 喝了受潮的奶粉怎么办 刚买的奶粉受潮怎么办 羊不小心吃了化肥怎么办 阿胶粉结成块了怎么办 半桶奶粉受潮了怎么办 眉粉受潮了结块怎么办 刚买的奶粉结块怎么办 袋装白糖成坨了怎么办 一袋子白糖硬了怎么办 粉饼上有一层油怎么办 葡萄后期氮肥施用过多怎么办 没洗的菜吃了怎么办 闻了汽油味头晕怎么办 碰到绿萝的汁液怎么办 吃了带农药水果怎么办 开槽模切一体机模切时开槽怎么办 柔版印刷走纸歪斜怎么办 美团外卖一天8单怎么办 单位显示器丢了怎么办员工赔