由于在同一游戏中,不同关卡在玩法上有许多相同的地方,为了能够让代码复用以简化编程,我往往会把相同的部分放在一个关卡原型里,让不同的卡关能够继承它。然而,javascript本身没有类与继承的概念,这就需要用其它的方法来模拟继承,其中,最为常见的一种方法就是使用原型(prototype)。

在网上查了些文章与例子之后,我在CocosBuiler中写下的代码大概是这样子的:

var Level1=function() {};
Level1.prototype=new LevelBase();
Level1.prototype.constructor=Level1;
Level1.prototype.onDidLoadFromCCB=function() {
    LevelBase.prototype.onDidLoadFromCCB.apply(this,arguments);
    //Do Something
};

第一次运行没问题,然而重玩本关的时候,在一个CCB节点的控制器类中报"Invalid Native Object"错误,报错的地方是调用了该控制器的rootNode的getPosition()函数:

this.rootNode.getPosition()

在C++中找到相应的报错语句是这样的:

JSObject *obj = NULL;
cocos2d::CCNode* cobj = NULL;
obj = JS_THIS_OBJECT(cx, vp);
js_proxy_t *proxy; JS_GET_NATIVE_PROXY(proxy, obj);
cobj = (cocos2d::CCNode *)(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2( cobj, cx, JS_FALSE, "Invalid Native Object");

这样看报错的原因只有可能是rootNode出了问题,于是把rootNode打印出来,结果显示确实是Sprite类型没错。那么是本地取this对象的时候出了问题?SpiderMonkey、CocosBuilder、cocos2d-x的Javascript binding都有可能与这个问题有关,从底层入手有点大海捞针的感觉,那么就从顶层的逻辑入手。

既然是重玩本关的时候出错,首先要考虑的就是有些地方没有释放,接下来做了些测试,发现把要继承的代码直接写在关卡类里面,不经过继承就不会出现这个问题,重新审视那段继承的代码,想会不会是因为用了LevelBase的实例作原型,导致这个LevelBase实例连带里面成员一起没有释放,所以才出现了这个问题。

抱着姑且一试的心情,把上面那段继承的代码改成:

var Level1=function() {
    LevelBase.apply(this,arguments);
    this.onDidLoadFromCCB=function() {
    LevelBase.prototype.onDidLoadFromCCB.apply(this,arguments);
    //Do Something
    };
};

Level1.prototype=LevelBase.prototype;
Level1.prototype.constructor=Level1;

运行,问题解决了!