CCRef ? arrgh! - Help!

Nearly 24 hours trying to figure this out, my game plays perfectly for maybe 3 minutes then breaks, something is trying to de-allocate somewhere. When I look at “this” in CCRef is just show me a de-allocated object. Is there a way to find out what???

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

I get the feeling you’re creating an autorelease object without properly retaining it, but hard to know without looking at your code. What I mean by not properly retaining it is something like:

  • Creating a node (or node subclass) without explicitly retaining or adding it as a child, then trying to refer to it later on
  • Creating an action (or action subclass) without explicitly retaining or running it, then trying to run it later on

Or something similar to the above two examples.

I think it’s somewhere in here:

        // remove any on screen
	for (shared_ptr<RemainingLife> remainingLife : _remainingLivesPlayer1) {
		this->removeChild(remainingLife.get());
	}
	_remainingLivesPlayer1.clear();

	// now add in the required number of remaining life indicators
	for (int i = 0; i < player1Lives; i++) {
		auto newRemainingLife = std::make_shared<RemainingLife>();
		_remainingLivesPlayer1.push_back(newRemainingLife);
		newRemainingLife->initWithPositionAndType(inherited::getGridPosition(20.0f + 5.0f * i, 2.0f), kRemainingLifeType1);
		newRemainingLife->retain();

		this->addChild(newRemainingLife.get(), GroundPlace::k5Top);
		newRemainingLife->autorelease();
	}

Though it might be you just can’t use shared_prt with cocos2dx. I’m really new to c++ and cocos2dx ? Is there an alternative type of structure ?

That section of code definitely looks like it could be the source of the problem, but without knowing too much about your custom classes there’s no way for me to know. Rather than trying to send you off on a wild goose chase by me guessing, let me just try and give you some cocos2d-x tips instead:

1. If you subclass a Cocos2d-x object that uses the create pattern, follow the create pattern in your subclass and use it to instantiate instances of your subclass.

It looks like your RemainingLife is some kind of Node subclass. Instead of instantiating it directly with std::make_ref, make a static RemainingLife::create method that looks something like this:

RemainingLife* RemainingLife::create() {
    auto life = new (std::nothrow) RemainingLife();
    if (life && life->init()) {
        life->autorelease();
        return life;
    }

    delete life;
    life = nullptr;
    return nullptr;	
}

2. Lean on Cocos2d-x’s built-in memory management when you can, instead of using things like shared_ptr.

Any class that follows the create pattern in Cocos2d-x is managed for you (provided you follow the proper usage patterns), so there usually isn’t a need to further manage them with things like shared_ptr. Sure, the autorelease stuff is a little strange when comparing it to typical C++ code (although it should be very familiar to Objective-C devs), but it’s not difficult to understand and works well.

  • Retain increases the reference count
  • Release decreases the reference count
  • Autorelease just means the object will be freed at the next time it’s safe to do so (e.g. the start of the next frame)

Using the create pattern, you can rewrite that section of code to something more like this:

// No need for a vector of shared_ptrs, all of the RemainingLife instances
// will be managed by CC2D (see below)
for (auto remainingLife : _remainingLivesPlayer1) {
	// Node::removeChild calls release
	this->removeChild(remainingLife);
}
_remainingLivesPlayer1.clear();

// now add in the required number of remaining life indicators
for (int i = 0; i < player1Lives; i++) {
	// RemainingLife::create() calls autorelease for you, no need to explicitly do it after instantiation
	auto newRemainingLife = RemainingLife::create();

	newRemainingLife->initWithPositionAndType(inherited::getGridPosition(20.0f + 5.0f * i, 2.0f), kRemainingLifeType1);

	// Node::addChild calls retain, there's no need to explicitly call it in this loop
	this->addChild(newRemainingLife.get(), GroundPlace::k5Top);

	// All you need to do is put the naked pointer in a vector, its memory is being managed by CC2D
	_remainingLivesPlayer1.push_back(newRemainingLife);
}

Sorry, I can’t really point you to exactly what’s going wrong with your code but hopefully some of this helps.

2 Likes

Brilliant, thanks that has help a lot. I will try using this method later tonight :slight_smile:

This worked a treat, what a great framework this is, also by having “CREATE_FUNC(RemainingLife)” in the header of RemainingLife.h it created the static method for me. It’s all now starting to make sense.