2013-05-12 26 views
0

將iOS中的cocos2d-x項目移植到Android時,我發現一個問題會導致Android崩潰,但不會導致iOS崩潰,爲了解決這個問題,我對HelloWorld示例做了一些小修改。要重現此問題,只需按下右下角的關閉按鈕,在Android上它會崩潰,但不會在iOS上。在iOS上導致崩潰的cocos2d-x代碼

致使所述崩潰的代碼:

void TestNode::test() 
{ 
    // This will cause crash on Android, but OK on iOS 
    CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy)); 
    this->runAction(selector); 

    // This is ok on both Android and iOS 
// CCCallFunc *selector = CCCallFunc::create(scene_, callfunc_selector(HelloWorld::destroyNode)); 
// scene_->runAction(selector); 

    // This is ok on both Android and iOS 
// destroy(); 
} 

完整的代碼如下所示:

HelloWorldScene.h

#ifndef __HELLOWORLD_SCENE_H__ 
#define __HELLOWORLD_SCENE_H__ 

#include "cocos2d.h" 

class TestNode : public cocos2d::CCNode { 
    public: 
     TestNode(cocos2d::CCLayer *scene); 
     ~TestNode(); 

     void test(); 
     void destroy(); 

     cocos2d::CCLayer *scene_; 
     cocos2d::CCSprite *sprite_; 
}; 

class HelloWorld : public cocos2d::CCLayer 
{ 
private: 
    TestNode *node_; 
public: 
    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone 
    virtual bool init(); 

    // there's no 'id' in cpp, so we recommend returning the class instance pointer 
    static cocos2d::CCScene* scene(); 

    // a selector callback 
    void menuCloseCallback(CCObject* pSender); 

    // implement the "static node()" method manually 
    CREATE_FUNC(HelloWorld); 

    void destroyNode(); 
}; 

#endif // __HELLOWORLD_SCENE_H__ 

HelloWorldScene.cpp:

#include "HelloWorldScene.h" 
#include "AppMacros.h" 
USING_NS_CC; 

TestNode::TestNode(cocos2d::CCLayer *scene): 
    scene_(scene) 
{ 
    sprite_ = CCSprite::create("CloseNormal.png"); 
    sprite_->setPosition(ccp(200, 200)); 
    scene_->addChild(sprite_, 255); 
} 

TestNode::~TestNode() 
{ 
    scene_->removeChild(sprite_, true); 
    scene_->removeChild(this, true); 
    CCLog("+++ ~TestNode"); 
} 

void TestNode::test() 
{ 
    // This will cause crash on Android, but OK on iOS 
    CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy)); 
    this->runAction(selector); 

    // This is ok on both Android and iOS 
// CCCallFunc *selector = CCCallFunc::create(scene_, callfunc_selector(HelloWorld::destroyNode)); 
// scene_->runAction(selector); 

    // This is ok on both Android and iOS 
// destroy(); 
} 

void TestNode::destroy() 
{ 
    CCLog("+++ destroy"); 
    delete this; 
} 

CCScene* HelloWorld::scene() 
{ 
    // 'scene' is an autorelease object 
    CCScene *scene = CCScene::create(); 

    // 'layer' is an autorelease object 
    HelloWorld *layer = HelloWorld::create(); 

    // add layer as a child to scene 
    scene->addChild(layer); 

    // return the scene 
    return scene; 
} 

// on "init" you need to initialize your instance 
bool HelloWorld::init() 
{ 
    ////////////////////////////// 
    // 1. super init first 
    if (!CCLayer::init()) 
    { 
     return false; 
    } 

    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); 
    CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); 

    ///////////////////////////// 
    // 2. add a menu item with "X" image, which is clicked to quit the program 
    // you may modify it. 

    // add a "close" icon to exit the progress. it's an autorelease object 
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
             "CloseNormal.png", 
             "CloseSelected.png", 
             this, 
             menu_selector(HelloWorld::menuCloseCallback)); 

    pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 , 
           origin.y + pCloseItem->getContentSize().height/2)); 

    // create menu, it's an autorelease object 
    CCMenu* pMenu = CCMenu::create(pCloseItem, NULL); 
    pMenu->setPosition(CCPointZero); 
    this->addChild(pMenu, 1); 

    ///////////////////////////// 
    // 3. add your codes below... 

    // add a label shows "Hello World" 
    // create and initialize a label 

    CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", TITLE_FONT_SIZE); 

    // position the label on the center of the screen 
    pLabel->setPosition(ccp(origin.x + visibleSize.width/2, 
          origin.y + visibleSize.height - pLabel->getContentSize().height)); 

    // add the label as a child to this layer 
    this->addChild(pLabel, 1); 

    // add "HelloWorld" splash screen" 
    CCSprite* pSprite = CCSprite::create("HelloWorld.png"); 

    // position the sprite on the center of the screen 
    pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y)); 

    // add the sprite as a child to this layer 
    this->addChild(pSprite, 0); 

    node_ = new TestNode(this); 
    this->addChild(node_); 

    return true; 
} 

void HelloWorld::menuCloseCallback(CCObject* pSender) 
{ 
// CCDirector::sharedDirector()->end(); 
    if (node_) { 
     node_->test(); 
     node_ = NULL; 
    } 

//#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) 
// exit(0); 
//#endif 
} 

void HelloWorld::destroyNode() 
{ 
    node_->destroy(); 
} 
+0

發佈錯誤消息和調用堆棧。不要以爲任何人都會運行你的代碼,絕對不是沒有整個項目的「開放和運行」格式。 – LearnCocos2D 2013-05-12 15:21:58

+0

正如@ baskus的建議,它是一個壞主意,銷燬CCCallFunc仍然保留指針的節點(參考http://www.parashift.com/c++-faq-lite/delete-this.html),我會重構代碼使用創建/發佈範例,而不是直接使用new/delete。 – ocean 2013-05-13 02:28:15

回答

2

我不知道爲什麼這可以在iOS上運行,但你不應該這樣做。當您運行代碼

CCCallFunc *selector = CCCallFunc::create(this, callfunc_selector(TestNode::destroy)); 
this->runAction(selector); 

它調用函數

void TestNode::destroy() 
{ 
    CCLog("+++ destroy"); 
    delete this; 
} 

要銷燬的對象的CCCallFunc動作仍然保持一個指針。然後,當CCCallFunc對象被銷燬時,它會運行CC_SAFE_RELEASE宏,它將在你的TestNode對象上調用release。但是,現在TestNode對象已經被釋放,這很可能是你崩潰的原因。