tolua被作为库使用时,首先会做大量内部初始化工作:

一、tolua_open是入口点,它创建很多用于管理的内部变量,以下用_G指代全局表,_R指定registry table:

1、_R.TOLUA_VALUE_ROOT={}, 这个表是cocos2dx自己加的,它把所有传入lua的cppobj/userdata都塞到这个表里,而且这还不是一个弱表,也就意味着cocos2dx创建的cpp obj,永远都不会被gc!只有在c++层面被delete时,才会去这个表里删除自己。因此,每一个cocos2dx cpp obj,是不可能依赖lua gc来释放的,必须纯手动管理(retain/release)。当然,根据cocos2dx自身的设计,每个obj在new出来refcount为1,并且会添加autorelease pool里去,因此只要它不在场景树上,下一帧就会被自动删除掉。通常情况下,lua代码是不会干涉其生命期的。但是有些时候我们会把某些节点从场景树上摘下,过一段时间又挂上去,这时就必须先调一下它的retain,如果之后忘记调release,那么它就永远不会销毁了。

2、_R.tolua_peers={},这是个弱表,用来缓存所有cppobj的ptr->peer table,所谓peer table,就是给每个cpp obj userdata关联的一个lua table,用来提供lua层面的额外的kv存储。tolua++把每个userdata的peer table设成该userdata的env,目的当然是为了方便找到它。因为在lua实现里,userdata的env是没有内定用途的,于是tolua++就拿来存peer table了。

3、_R.tolua_ubox={},也是个弱表,用来缓存所有cppobj的ptr->userdata映射,userdata里面存放的值其实就是ptr。这个表的用途是记录所有已经push到lua里的cppobj,每个cppobj第一次进入lua时,会去做创建userdata、关联metatable、设置peertable(env)等等一大堆操作,然后把ptr->userdata的映射关系记到这个表里,下次再被返回进lua时,就从这表里去查找,查到的话就直接拿已创建好的userdata用了。但是有一种特殊情况,就是第二次push时的类型是上一次的子类,也就是一个“特化类型”,那么需要改设metatable,以使子类的新函数能被访问到。

4、_R.tolua_super={},用来记录每个类型的所有基类,key是子类的mt,value则是个map,其中每个kv都是一个pair<基类名,1>。通过这个表可以快速知道两个类之间有无继承关系。

5、_R.tolua_gc={}

6、_R.tolua_gc_event = closure{ func:class_gc_event, upvalue:上述两个表格 }, 这是挂在每个类对应的metatable上的__gc方法。

7、_G.tolua={},里面存放tolua自己的一些工具函数

二、类的注册。

1、对每个用户类,首先要用tolua_usertype声明这是个用户类型:

tolua_usertype(tolua_S, "WebSocket")

它里面的做事情很简单,就是分别为type和const type“两个类”建表(这个表也就是其实例userdata的元表),然后设置type继承const type,从数据的角度来看也就是:

//先用tolua_newmetatable分别创建创建两个元表,其内又调用tolua_classevents挂上各种属性

_R["WebSocket"]={

__index = cfunc:class_index_event,

__newindex = cfunc:class_newindex_event,

__gc=_R.tolua_gc_event //上面之<一.6>

//其它各种add/sub/lt/eq等方法……

}

_R["const WebSocket"]= ……同上

//再用mapsuper(L,type,ctype) 设置两者的继承关系

_R.tolua_super[_R["WebSocket"]] ={ "const WebSocket" = 1,} //上面之<一.4>

//这个过程会递归执行,即把基类的所有基类都添到子类里

for k,v in pairs(_R.tolua_super[_R["const WebSocket"]]) do

  _R.tolua_super[_R["WebSocket"]][k] = v

end

//在这一步里,mapsuper只是设置type->const type,但是在后续步骤里会添加大量type->base type,因此递归下来,每个type的_R.tolua_super[type]表还是有很多内容的。

2、然后用tolua_cclass来注册类。tolua_cclass主要做两件事,一是把基类和父类(以及各const变种)之间的关系建立起来,二是注册类的析构函数(构造函数由普通的create静态函数替代了)。

关于继承关系,总共四对:

mapinheritance(L,name,base);

mapinheritance(L,cname,name);

mapsuper(L,cname,cbase);

mapsuper(L,name,base);

其中c指const。除了上面提到的mapsuper,又来了个mapinheritance, 再次对比说明下:

mapsuper是:在_R.tolua_super记录每个类(k)有哪些父类(v),所有父类以v[类名]=1的形式记录着。

mapinheritance是:把父类表设成基类表的元表,同时给基类表上挂一个用以记录该类objptr->userdata映射的弱表,大致是:

setmetatable( _R.type, _R.base_type ),

_R.type.tolua_ubox = _R.base_type.tolua_ubox or weak({})

其中第二句与前述之<一.3>有点相似,只是那是放在_R上的一个总表,而这里是为每个类单独建表,但子类与基类是共用的,也就是每次调用tolua_cclass注册一个类,就有“3个类”的表中的tolua_ubox字段指向了同一个表,这3个类从上到下是:base type -> type -> const type,至于const base type,那是在之前注册基类时处理的了。不过我觉得这个设定很无用,一个tolua_ubox总表就足够了,为每个类分别再建一个有什么特殊作用?从读代码结果来看,没有!所有使用到它的地方,都是先去_R.type.tolua_ubox上找,如果找不到再找_R.tolua_ubox,而在哪里找到对后续操作并没有差别。接着我试了下修改代码,把所有针对_R.type.tolua_ubox的语句都删掉,直接使用总表里。结果,程序一样跑起来了,反复进场出场导致大量对象创建销毁,也没发现什么问题!

不过这里还有个另外的问题!注意和第2条里的对比:

mapinheritance(L,cname,name) //tolua_cclass里

mapsuper(L,type,ctype) //tolua_usertype里

到底type和const type谁是“基类”呢?这主要看不同场合里“基类”这个概念是用于解决什么问题了:

对mapsuper而言:在c++里一个声明为const的参数,实际是对函数本身的约束而不是对实参的约束:它只是强调函数内部不会去修改这个参数,至于传进来的实参本身是不是const的根本不重要,因为反正函数已经承诺不会去修改它了。所以要把一个类型为type的obj传给某个带有const type参数的函数,是没有问题的,但反过来,一个const type对象要传给接受type参数的函数是不行的,因为不知道它到底会不会修改(没有承诺不修改就意味着会修改)。再加上tolua++在生成胶水代码时,对每个参数都要做类型匹配检测(也就是在生成代码中大量的lua_isusertype调用),一个usertype变量是否合格,就是检查它所在位置的参数类型,是否是它可以“扮演”的角色,这些角色一是它的所有父类,二就是它以及所有父类的const变种了,而这些可以扮演的类型,恰好就是mapsuper所建立的_R.tolua_super体系中记录的内容了。

对mapinheritance而言:它将基类表设成子类表的元表,这是为了在子类表里可以找到基类的函数(在class_index_event函数里,有一个while循环,通过这里建立的链条不断向上级查找)。就这个目的而言,type和const type谁做基类是一样的。但是,type还有真正的基类base,按照base->type->const type的继承顺序是恰好满足的:

classA -> const classA

-> classB -> const classB

-> classC -> const classC
                                       -> classD -> const classD

也就是说const类型在当前层次的链上是一个叶,下一层次不是从它继承,而是与它并级。

而如果按照base->const type->type->const subtype->sub type,那么问题就麻烦了,因为在注册函数时,所有函数都是挂在不带const的类表上的,如CCNode的函数都在_R["CCNode"]里,这也符合脚本里创建类实例时的习惯:直接以“纯粹的”(不带const的)类名来操作,比如CCNode:create,而不会写作const_CCNode:create()。那么在后者的继承链上,每一个const type实际成了断点,没有得到这个type自身的函数!

cocos2dx之tolua++全面分析(二):类注册的更多相关文章

  1. cocos2dx之tolua++全面分析(一):tolua++工具本身

    在cocos2dx/tools/tolua++下面,有大量pkg文件,这些是按tolua++要求格式写好的.需要导出到lua中的c++类描述文件. 每当在c++类里增加了新函数需要导出时,应同步修改相 ...

  2. 【转】Cocos2d-x下Lua调用自定义C++类和函数的最佳实践

    转自:http://segmentfault.com/blog/hongliang/1190000000631630 关于cocos2d-x下Lua调用C++的文档看了不少,但没有一篇真正把这事给讲明 ...

  3. Cocos2d-x下Lua调用自定义C++类和函数的最佳实践[转]

    Cocos2d-x下Lua调用C++这事之所以看起来这么复杂.网上所有的文档都没讲清楚,是因为存在5个层面的知识点: 1.在纯C环境下,把C函数注册进Lua环境,理解Lua和C之间可以互相调用的本质 ...

  4. Scala词法文法解析器 (二)分析C++类的声明

    最近一直在学习Scala语言,偶然发现其Parser模块功能强大,乃为BNF而设计.啥是BNF,读大学的时候在课本上见过,那时候只觉得这个东西太深奥.没想到所有的计算机语言都是基于BNF而定义的一套规 ...

  5. MapReduce深度分析(二)

    MapReduce深度分析(二) 五.JobTracker分析 JobTracker是hadoop的重要的后台守护进程之一,主要的功能是管理任务调度.管理TaskTracker.监控作业执行.运行作业 ...

  6. 框架-springmvc源码分析(二)

    框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...

  7. Linux spi驱动分析(二)----SPI核心(bus、device_driver和device)

    一.spi总线注册 这里所说的SPI核心,就是指/drivers/spi/目录下spi.c文件中提供给其他文件的函数,首先看下spi核心的初始化函数spi_init(void).程序如下: 点击(此处 ...

  8. Spring5源码深度分析(二)之理解@Conditional,@Import注解

    代码地址: 1.源码分析二主要分析的内容 1.使用@Condition多条件注册bean对象2.@Import注解快速注入第三方bean对象3.@EnableXXXX 开启原理4.基于ImportBe ...

  9. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

随机推荐

  1. maven导入项目时,缺少部分source folder

    今天导入公司的maven项目时,少了一些source folder,运行启动正常,但是页面打不开,找不到对应的目录文件,使用maven更新项目,重启编辑器也无效. 问题描述如图所示,缺少了图中的2个目 ...

  2. 期刊搜索问题——SCI、EI、IEEE和中文期刊

    1.SCI.EI收录是什么意思? SCI和EI都是收录,并不是实体的期刊出版社,相当于具有高品质期刊出版社的合体(或统充),隔一段时间(几年或者几个月)SCI会进行评估,哪些出版社不具有被SCI收录的 ...

  3. Redis高级进阶(二)

    一.消息通知 在一些网站上,经常会有一些发布/订阅或者邮件订阅的功能,尤其一些博客上.其实这种问题很常见,当页面需要进行如发送邮件.复杂的计算时会阻塞页面的渲染.为了避免用户等待太久,应该使用其他进程 ...

  4. 20145210姚思羽《网络对抗技术》逆向及Bof基础实践

    20145210姚思羽<网络对抗技术>逆向及Bof基础实践 实践目标 1.本次实践的对象是一个名为pwn1的linux可执行文件. 2.该程序正常执行流程是:main调用foo函数,foo ...

  5. 针对PHP性能方面编程技巧的总结

    注:本文为作者多年工作经验总结,转贴请注明出处 Albert Wang 做PHP已经有4年多了吧,总是没写点什么总结一下,现在开始写博客了,也顺便总结下吧,留给那些新入行的朋友看看. 1. 不宜频繁使 ...

  6. spring boot: 在maven中装入springframework框架

    1.在maven 的pom.xml中加入 <dependency> <groupId>org.springframework</groupId> <artif ...

  7. 模拟Windows任务管理器CPU使用率的动态折线图-农夫山泉

    Delphi的TCanvas类可以实现各种复杂的图形输出功能,基于近期项目的需求,利用它实现了一个很炫的动态折线图(模拟了资源管理器中CPU使用率的折线图),可以直观地展现出数值的实时变化情况. 这段 ...

  8. DBGrid1

    A.ShowMessage(IntToStr(Column.Index));   B.ShowMessage(IntToStr(dbgrid1.SelectedField.Index)); .得到当前 ...

  9. 1W字看懂互联网知识经济

    互联网知识经济,发生关键变化的不是知识,而是经济. 今天和大家分享方军老师的新书<付费:互联网知识经济的兴起>.这本书是一个老互联网人看新知识经济,很有意思的视角在于,以互联网的发展为节点 ...

  10. C#winform拖拽实现获得文件路径

    1.关键知识点说明: 通过DragEnter事件获得被拖入窗口的“信息”(可以是若干文件,一些文字等等),在DragDrop事件中对“信息”进行解析.窗体的AllowDrop属性必须设置成true;且 ...