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

前端之家收集整理的这篇文章主要介绍了cocos2d-x LUA 2.x里tolua++的一个隐患前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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

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

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

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

  1. tolua_pushusertype(tolua_S,tolua_obj,"CCSize");
  2. 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文件的最上面,就有以下的代码

  1. local CCObjectTypes = {
  2. "CCObject","CCAction","CCImage","CCFiniteTimeAction",.......
  3. "
  4. }
  5. -- register CCObject types
  6. for i = 1,#CCObjectTypes do
  7. _push_functions[CCObjectTypes[i]] = "toluafix_pushusertype_ccobject"
  8. end

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

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

以下是toluafix_pushusertype的代码

  1. void tolua_pushusertype_internal (lua_State* L,void* value,const char* type,int addToRoot)
  2. {
  3. if (value == NULL)
  4. lua_pushnil(L);
  5. else
  6. {
  7. luaL_getMetatable(L,type); /* stack: mt */
  8. if (lua_isnil(L,-1)) { /* NOT FOUND Metatable */
  9. lua_pop(L,1);
  10. return;
  11. }
  12. lua_pushstring(L,"tolua_uBox");
  13. lua_rawget(L,-2); /* stack: mt uBox */
  14. if (lua_isnil(L,-1)) {
  15. lua_pop(L,1);
  16. lua_pushstring(L,"tolua_uBox");
  17. lua_rawget(L,LUA_REGISTRYINDEX);
  18. };
  19. lua_pushlightuserdata(L,value); /* stack: mt uBox key<value> */
  20. lua_rawget(L,-2); /* stack: mt uBox uBox[value] */
  21. if (lua_isnil(L,-1))
  22. {
  23. lua_pop(L,1); /* stack: mt uBox */
  24. lua_pushlightuserdata(L,value);
  25. *(void**)lua_newuserdata(L,sizeof(void *)) = value; /* stack: mt uBox value newud */
  26. lua_pushvalue(L,-1); /* stack: mt uBox value newud newud */
  27. lua_insert(L,-4); /* stack: mt newud uBox value newud */
  28. lua_rawset(L,-3); /* uBox[value] = newud,stack: mt newud uBox */
  29. lua_pop(L,1); /* stack: mt newud */
  30. /*luaL_getMetatable(L,type);*/
  31. lua_pushvalue(L,-2); /* stack: mt newud mt */
  32. lua_setMetatable(L,-2); /* update mt,stack: mt newud */
  33. #ifdef LUA_VERSION_NUM
  34. lua_pushvalue(L,TOLUA_NOPEER); /* stack: mt newud peer */
  35. lua_setfenv(L,-2); /* stack: mt newud */
  36. #endif
  37. }
  38. else
  39. {
  40. /* check the need of updating the Metatable to a more specialized class */
  41. lua_insert(L,-2); /* stack: mt uBox[u] uBox */
  42. lua_pop(L,1); /* stack: mt uBox[u] */
  43. lua_pushstring(L,"tolua_super");
  44. lua_rawget(L,LUA_REGISTRYINDEX); /* stack: mt uBox[u] super */
  45. lua_getMetatable(L,-2); /* stack: mt uBox[u] super mt */
  46. lua_rawget(L,-2); /* stack: mt uBox[u] super super[mt] */
  47. if (lua_istable(L,-1))
  48. {
  49. lua_pushstring(L,type); /* stack: mt uBox[u] super super[mt] type */
  50. lua_rawget(L,-2); /* stack: mt uBox[u] super super[mt] flag */
  51. if (lua_toboolean(L,-1) == 1) /* if true */
  52. {
  53. lua_pop(L,3); /* mt uBox[u]*/
  54. lua_remove(L,-2);
  55. return;
  56. }
  57. }
  58. /* type represents a more specilized type */
  59. /*luaL_getMetatable(L,type); // stack: mt uBox[u] super super[mt] flag mt */
  60. lua_pushvalue(L,-5); /* stack: mt uBox[u] super super[mt] flag mt */
  61. lua_setMetatable(L,-5); /* stack: mt uBox[u] super super[mt] flag */
  62. lua_pop(L,3); /* stack: mt uBox[u] */
  63. }
  64. lua_remove(L,-2); /* stack: uBox[u]*/
  65. if (0 != addToRoot)
  66. {
  67. lua_pushvalue(L,-1);
  68. tolua_add_value_to_root(L,value);
  69. }
  70. }
  71. }


以下是toluafix_pushusertype_ccobject方法代码

  1. TOLUA_API int toluafix_pushusertype_ccobject(lua_State* L,int refid,int* p_refid,void* ptr,const char* type)
  2. {
  3. if (ptr == NULL || p_refid == NULL)
  4. {
  5. lua_pushnil(L);
  6. return -1;
  7. }
  8. if (*p_refid == 0)
  9. {
  10. *p_refid = refid;
  11. lua_pushstring(L,TOLUA_REFID_PTR_MAPPING);
  12. lua_rawget(L,LUA_REGISTRYINDEX); /* stack: refid_ptr */
  13. lua_pushinteger(L,refid); /* stack: refid_ptr refid */
  14. lua_pushlightuserdata(L,ptr); /* stack: refid_ptr refid ptr */
  15. lua_rawset(L,-3); /* refid_ptr[refid] = ptr,stack: refid_ptr */
  16. lua_pop(L,1); /* stack: - */
  17. lua_pushstring(L,TOLUA_REFID_TYPE_MAPPING);
  18. lua_rawget(L,LUA_REGISTRYINDEX); /* stack: refid_type */
  19. lua_pushinteger(L,refid); /* stack: refid_type refid */
  20. lua_pushstring(L,type); /* stack: refid_type refid type */
  21. lua_rawset(L,-3); /* refid_type[refid] = type,stack: refid_type */
  22. lua_pop(L,1); /* stack: - */
  23. //printf("[LUA] push CCObject OK - refid: %d,ptr: %x,type: %s\n",*p_refid,(int)ptr,type);
  24. }
  25. tolua_pushusertype_and_addtoroot(L,ptr,type);
  26. return 0;
  27. }

通俗点的说,

toluafix_pushusertype是有隐患的,就好像我刚刚所说的,在各种复杂条件配合下,例如在某一段代码开了定时器,定时器逻辑里的对象又有复杂的继承树,并又使用了isInstance这种功能,或者还有其它一些我也想不明白的条件,就算明明在c++里创建了一个对象,并绑定到lua的时候,if (lua_isnil(L,-1))这段代码会返回false,直接导致报错。 所以,cocos2d-x的作者们很聪明地发明了toluafix_pushusertype_ccobject,只要我们完全按照basic.lua一样,将自己新建的继承CCObject的类,都配置在basic.lua里,就绝对不会出现我上面所说的问题。

猜你在找的Cocos2d-x相关文章