ページ

2015年3月17日火曜日

Cocos2d-xにおけるメモリ管理方法


Cocos2d-xでのメモリ管理方法について、自分の理解を整理するためにメモ。

執筆時のバージョン


Cocos2d-x v3.4.2

参照カウント


Cocos2d-xではメモリ管理の方法として、参照カウント方式を採用しています。

参照カウントはガベージコレクション戦略の1つで、その概要は以下の通りです。

  • オブジェクトに対して、参照カウントと呼ばれる整数値を付加しておく。
  • オブジェクトに対する参照がどこかに保存されたら参照カウントをインクリメントし、オブジェクトに対する参照が削除されたらデクリメントする。
  • 参照カウントがゼロになったら、オブジェクトへの最後の参照が削除されたことになり、オブジェクトを破棄する。

Refクラス


これをCocos2d-xではRefクラスに実装しており、Nodeクラスなどほとんどのクラスの基底クラスになっているようです。

Refクラスのメンバは以下の通り。


Public Member Functions


void retain () 参照カウンタを+1。
void release () 参照カウンタを-1。0になったらオブジェクトは破棄される。
Ref * autorelease () オブジェクトをAutoreleasePoolに登録。自動的に破棄されるようになる。
unsigned int getReferenceCount () const 参照カウンタを取得。
virtual ~Ref () デストラクタ。


Protected Member Functions


Ref () コンストラクタ。参照カウンタを1で初期化する。


Protected Attributes


unsigned int _referenceCount 参照カウンタ。


参照カウンタ_referenceCountを、retain()とrelease()で操作します。

AutoreleasePool


私はiOSアプリの開発経験はないのですが、iOS SDKではNSAutoreleasePoolが参照カウント方式のメカニズムをラップしているらしいです。

これをCocos2d-xでもAutoreleasePoolとしてクローンしています。

代わりにstd::shared_ptrを使おうと試みたりもしたそうですが、パフォーマンスの問題で断念したとのこと。

AutoreleasePoolの仕組みはこうです。

  1. object->autorelease()を呼び出した時、このオブジェクトはAutoReleasePoolに登録される。
  2. AutoReleasePoolはこのオブジェクトのライフサイクルを現在のフレームの終了まで保持する。
  3. 現在のフレーム(スタックフレームのこと?)の終了時に、このオブジェクトが他のクラスやコンテナに保持されていなければ、自動的に破棄する。

例えば、layer->addChild(sprite)とすると、spriteはlayerの子リストに追加されます。

この場合、spriteのライフサイクルは現在のフレームの終わりではなく、layerが破棄されるまでとなります。

create関数


RefクラスのAPIを見れば分かるように、Refクラスのコンストラクタはprotectedであるため直接newすることはできません。

ではどうするのかというと、代わりにcreateおよびcreateから始まるスタティックなメンバ関数が用意されています。

この関数の処理の概要は以下の通りです。

  • 新しくインスタンスを生成する。
  • 必要な初期化を行う。
  • autorelease()を呼び出す。
  • 生成したインスタンスへのポインタを返却する。

create関数を使うことで、忘れずにautorelease()を呼び出し、AutoReleasePoolに登録するわけですね。

試しに1つ実装を見てみます。

以下は、Nodeクラスのcreat関数の実装です。

Node * Node::create()
{
    Node * ret = new (std::nothrow) Node();
    if (ret && ret->init())
    {
        ret->autorelease();
    }
    else
    {
        CC_SAFE_DELETE(ret);
    }
    return ret;
}

LayerやSpriteなど、既存のクラスを継承して独自のクラスを定義するとき、create関数にあたるものを作ることになりますが、その場合はCREATE_FUNCが用意されているので、引数が不要な場合はこれを使います。

引数が必要な場合も、既存のcreateを参考にすれば良さそうです。

まとめ

  • cocos2d-xではメモリ管理は参照カウント方式。
  • Refクラスがそのためのクラス。多くのクラスの基底クラスになっている。
  • retain()とrelease()を使ってリファレンスカウントの管理を行う。
  • release()を忘れずに行うために、AutoReleasePoolを使う。
  • AutoReleasePoolにオブジェクトを登録すると、スタックフレームの終了時に自動で解放してくれる。
  • AutoReleasePoolへの登録は、autorelease()を呼び出すことで行う。
  • autorelease()はcreate関数の中で呼び出される。
  • cocos2d-xのオブジェクトを生成するときはcreate関数を使う。

0 件のコメント:

コメントを投稿