忍者ブログ
  • 2024.08
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 2024.10
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

【2024/09/12 00:15 】 |
cocos2d-xの参照カウンタについて
cocos2d-xにおいては、すべてのクラスは
cocos2d::Ref
の子孫クラスである。
Refクラスは、参照カウンタを扱っている。

Refクラスのautorelease()メソッドを呼んでおくと、
Refとその子孫クラスのインスタンスを生成したとき、破棄処理を自分でしなくても
ゲームのメインループが来たときに自動で破棄してくれる。

CCRef.cpp

Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}



CCDirector.cpp

void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();

// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}



CCAutoReleasePool.cpp

void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = true;
#endif
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = false;
#endif
}



CCRef.cpp

void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount;

if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto poolManager = PoolManager::getInstance();
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
{
// Trigger an assert if the reference count is 0 but the Ref is still in autorelease pool.
// This happens when 'autorelease/release' were not used in pairs with 'new/retain'.
//
// Wrong usage (1):
//
// auto obj = Node::create(); // Ref = 1, but it's an autorelease Ref which means it was in the autorelease pool.
// obj->autorelease(); // Wrong: If you wish to invoke autorelease several times, you should retain `obj` first.
//
// Wrong usage (2):
//
// auto obj = Node::create();
// obj->release(); // Wrong: obj is an autorelease Ref, it will be released when clearing current pool.
//
// Correct usage (1):
//
// auto obj = Node::create();
// |- new Node(); // `new` is the pair of the `autorelease` of next line
// |- autorelease(); // The pair of `new Node`.
//
// obj->retain();
// obj->autorelease(); // This `autorelease` is the pair of `retain` of previous line.
//
// Correct usage (2):
//
// auto obj = Node::create();
// obj->retain();
// obj->release(); // This `release` is the pair of `retain` of previous line.
CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
}
#endif

#if CC_USE_MEM_LEAK_DETECTION
untrackRef(this);
#endif
delete this;
}
}



CCAutoReleasePool.cppを見るとわかるが、_managedObjectArrayに格納されているすべてのobjectに対してrelease()を実行している。
ずいぶん潔いやり方だ。生成しても放っておいてよい、というか放っておかなくてはいけない。自分でrelease()とか呼ぶと却ってバグる。
その使い方についてはCCRef.cppのrelase()メソッド内に詳しく書いてある。


一言で言うと
new/retain() と autorelease()/release() を対で呼び出さなければならない。
ということだ。

生成時にnewが呼ばれる。
cocos的には、Node::create()やSprite::create()などのように、static関数のcreate()メソッドが相当。create()の中でnewをしている。
例としてNode::create()を見てみると


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


となっている。
createの中で new と autorelease() を両方やっている。

それ以外にも、retain()を呼ぶならrelease()かautorelease()をどこかで呼んでおく。
対で呼び出されるようになっていれば、メモリリークは起こらない。

autorelease()は複数回呼び出しても問題ないようだ。autoreleasePoolに2回登録されるので、2回releaseが走る。


なお、Nodeインスタンスの場合、別のNodeの子になる(addChild)と参照カウンタが1増える。

parentNode->addChild(childNode); //childNodeの参照カウンタが1増える



これは、parentNodeのメンバ変数
Vector<Node*> _children;
に追加されるから。
cocos2d-xのVectorクラスは、pushBack(またはinsert,replace)したときにretain()を呼ぶようになっている。
このため、addChildされた子ノードは破棄されずに残る。


PR
【2014/12/09 13:13 】 | cocos2d-x | 有り難いご意見(0)
<<cocos2d-x 3.0 の主なクラス | ホーム | cocos2d-xのアニメーション>>
有り難いご意見
貴重なご意見の投稿














<<前ページ | ホーム | 次ページ>>