天天看点

JSB内存管理

jsb的内存管理

基于cocos2d-x 2.15,但同样适用于cocos2d-x 3.0。

jsb对象的生命周期

总所周知,javascript有自己的内存管理机制,即垃圾回收。cocos2d-x模拟垃圾回收系统来管理cocos对象。但这里有一个问题,就是将cocos2d-x对象绑定到javascript对象时由谁负责内存管理。

先看一个案例。

通过xxx.create()来分配对象内存

下面的代码分配了一个全局变量。

gnode并没有通过addchild()添加到其它cc.node中。

在菜单项的回调函数中添加如下代码:

当点击此按钮,你将会看到如下错误信息:

发生了什么!“invalid native object”是什么意思?

在javascript中gnode是一个全局变量,意味着它不能被回收。

但是gnode里的ccnode会被cocos2d-x回收。

为了弄清楚这个问题,你需要了解一下spidermonkey并深入研究javascript的绑定代码。

cc.node.create()的内部实现

详细的实现代码如下:

cc.node.create()被对应的c函数是js_cocos2dx_ccnode_create(),如下:

通过cocos2d:ccnode::create()成功分配的对象将会被封装成js_get_or_create_proxy()创建的一个新对象js_proxy_t。

在js_get_or_create_proxy()函数里,只要关注下面这行代码:

这段代码将一个jsobject添加到垃圾回收器的根集合中的spidermonkey api。proxy->obj是对应javascript里的一个jsobject。

所以通过cc.node.create()分配的对象将会一直保留在内存中,直到调用js_removeobjectroot()。

但是cocos2d::ccnode::create()是一个自动释放对象,它会在下一个游戏帧被cocos2d-x回收。

ccobject的析构函数将会被调用,请注意下面的代码:

pengine->removescriptobjectbyccobject 做了一件神奇的事情。

js_removeobjectroot函数将jsobject从javascript根集合中移除。 jsb_remove_proxy将proxy(委托)从hash表中移除。

现在我们可以解释本文开始提出的问题了。

cocos2d-x的垃圾回收系统负责内存管理

回到gnode,它是一个全局变量。ccobject的析构函数js_removeobjectroot的作用只是平衡js_addobjectroot的创建。spidermonkey将不会回收这个全局变量,但是gnode的本地对象将会被释放。访问gnode的本地对象将会产生之前看到的那个错误。

通过new分配对象

思考一下下面的代码:

为了找到正确答案,同样需要深入研究jsb代码。

如之前提到的,cc.node的构造函数是js_cocos2dx_ccnode_constructor()。

请注意下面的代码:

本地对象被压入到cocos2d-x的自动释放池中。所以new和create()是一样的。

关于retain()和release()

有两个函数可以用于手动控制对象的生命周期。如果你想避免之前例子中产生的错误。

你可以有以下两个选择:

1.将gnode添加到其它的ccnode中,addchild()将gnode保留在内部。

2.在create()之后立即调用gnode.retain()。

在第二种情况下,你需要在合适的时候调用gnode.release()以防止内存泄漏。下一节将会介绍它。

ctor()和onexit()

cocos2d-x jsb使用simple javascript inheritance by john resig。但是构造函数的名字不一样。

在jsb中,ctor是构造函数。对应的onexit则扮演的是析构函数,它会在ccnode释放之前被调用。

下面的例子演示了如何手动控制jsb对象的生命周期。