ナクナイ

勉強用の備忘録

[C++][cocos2d-x] Assert failed: reference count should greater than 0 や Thread 1:EXC_BAD_ACCESS のエラー

cocos2d-x でアプリ開発をしているとき、タイトルのようなエラーになってはまったので、その解決方法をメモ。

シーンからシーンへ切り替える際、都度 replaceScene を書きたくなかったので、
シーン切り替え用の関数を下記のように作成。


ヘッダーファイルの宣言 (BaseLayer.h)

class BaseLayer : public cocos2d::Layer
{
public:
    void gotoNextScene(float time);
    void setNextScene(cocos2d::Scene *scene);
    
private:
    cocos2d::Scene *next_scene;
};


cpp ファイルの定義 (BaseLayer.cpp)

void BaseLayer::gotoNextScene(float time)
{
    cocos2d::TransitionCrossFade *crossFade = cocos2d::TransitionCrossFade::create(0.5f, this->next_scene);
    cocos2d::Director::getInstance()->replaceScene(crossFade);
}

void BaseLayer::setNextScene(cocos2d::Scene *next_scene)
{
    this->next_scene = next_scene;
}


呼び出しもとの cpp ファイル (SplashScene.cpp)

bool SplashScene::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Point origin = Director::getInstance()->getVisibleOrigin();

    // スプラッシュ画面の生成
    auto label = LabelTTF::create("splash", "Arial", 80);
    label->setPosition(Point(origin.x + visibleSize.width/2,
                             origin.y + visibleSize.height - label->getContentSize().height));
    this->addChild(label, 1);
    
    // ↓ここ↓
    this->setNextScene(StartScene::createScene());
    this->scheduleOnce(schedule_selector(StartScene::gotoNextScene), 3);

    return true;
}

こんな感じで実行したら、ビルドは通るのに実行するとエラーになってしまった。
しかも、実行するたびにたまにエラー内容が変化するという悲しい現象。具体的には、

cocos2d/cocos/base/CCRef.cpp

void Ref::retain()
{
     CCASSERT(_referenceCount > 0, "reference count should greater than 0"); // ←ここ                                    
     ++_referenceCount;
}

で引っかかって

cocos2d: Assert failed: reference count should greater than 0
Assertion failed: (_referenceCount > 0), function retain, file

のエラーが出たり・・・・

cocos2d/cocos/2d/CCTransition.cpp

// custom onEnter
void TransitionScene::onEnter()
{
    Scene::onEnter();

    // disable events while transitions
    _eventDispatcher->setEnabled(false);

    // outScene should not receive the onEnter callback
    // only the onExitTransitionDidStart
    _outScene->onExitTransitionDidStart();

    _inScene->onEnter(); // ←ここ                                                                                    
}

でひっかかって

Thread 1:EXC_BAD_ACCESS (code=2, address=0xb0000160)

のようなエラーが出たり・・・。


ぐぐって調べていくと、どうやらメモリ管理がうまくできてないことが原因ということはあたりがついた。

最初のエラーに関しても、リファレンスカウントに関するエラーなので、おそらくすでにリリース済みのメモリ(もしくははじめから保持に失敗しているメモリ)に対して、メモリを取得しようとしている、ということのはず。


そこで、明示的にリファレンスカウントを retain(あげる)、release(さげる) してあげることで解決した。


具体的には、SplashScene.cpp を下記を追記。

void BaseLayer::gotoNextScene(float time)
{
    cocos2d::TransitionCrossFade *crossFade = cocos2d::TransitionCrossFade::create(0.5f, this->next_scene);
    cocos2d::Director::getInstance()->replaceScene(crossFade);
    this->next_scene->release(); // ここで release
}

void BaseLayer::setNextScene(cocos2d::Scene *next_scene)
{
    this->next_scene = next_scene;
    this->next_scene->retain(); // ここで retain
}


ただし、
iOS 開発で、EXC_BAD_ACCESS とさよならするための6つのルール | Zero4Racer PRO Developer's Blog
を見ると正しい対応ではない予感はすごくしている。

とりあえずいまはこのばんそこ対応で我慢して、もっと勉強して理解が深まったらちゃんと解決するとしよう。。
有識者の方いらっしゃったらコメント欄に残していただけるとうれしいです。


おもに参考にしたページ

  • リファレンスカウントの概念について

参照回数によるメモリ管理

  • iOS のメモリ管理について

iOS 開発で、EXC_BAD_ACCESS とさよならするための6つのルール | Zero4Racer PRO Developer's Blog

[Xcode] Xcode で開発したソースが自動的にローカルリポジトリに反映されるのを防ぐ

Xcode(5.1) で開発していたら、自動的に git のローカルリポジトリに反映されてしまった。
都度自分で commit やら add やら rm やらしたい。

Xcode の「Preferences」から Source Control を選択。

  • Refresh local status automatically
  • Refresh server status automatically
  • Add and remove files automatically


これらのチェックを全てはずして解決

[cocos2dx][Xcode][C++] Undefined symbols for architecture i386 とか linker command failed with exit code 1 でエラー

cocos2dx で、タップを検知したいと思い、ヘッダーファイルに下記の記述を行いました。

// タッチ開始時
virtual bool onTouchBegan(cocos2d::Touch* pTouch, cocos2d::Event* pEvent);

しかしビルドすると

f:id:tamulapin:20140328175504p:plain

というエラー。。。


エラー内容でぐぐってみると、
プログラミング雑記: Undefined symbols for architecture i386

Cocos2dxで"Undefined symbols for architecture i386:"が発生した場合の対処法 - たけぞうBLOG

「clang: error: linker command failed with exit code 1」というエラーへの対処 - Action Script書きの日記

Cocoaの日々: ld: library not found for -lz.1.2.3 と出たら

Xcode4.4にアップデートしたら「linker command failed with exit code 1」エラー?! | 個人で作るiPhoneアプリ制作体験記

とかとか、いろいろ見つかったけどぜんぜん解決せず。

結局、ヘッダーファイルに宣言をするだけではだめで、cpp ファイルにて定義まで行わないとエラーになるらしいです。下のように記述し、無事解決しました。

bool TouchTest::onTouchBegan(Touch* pTouch, Event* pEvent) {
    return true;
}


C++ の関数のプロトタイプ宣言というものは、それを記述しただけではエラーになるっていうことなのかな??
初心者丸出し。ちくしょう。。(T-T)

[Xcode][Cocos2d-x] Resouces ディレクトリ以下の画像が読み込まれない

Xcode で cocos2d-x を使い、画像を表示させてみようとしました。

PROJECTNAME/Resources 以下に finder 経由で画像を配置して

auto sprite = Sprite::create("tamulapin.png");
sprite->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

みたいな感じで記述してビルドすると、

cocos2d: cocos2d: fullPathForFilename: No file found at tamulapin.png. Possible missing file.

と怒られてエラーになりました。
Xcode のツリー構造で確認してみると、たしかに確認できません。

ツリー構造の「Resources」を右クリックし、「Add Files to PROJECTNAME」をクリックし、必要なファイルを add して解決。
それまではフルパスで記述してました。。

おわり。