replaceScene = CRASH : EXC BAD ACCESS - BOX2D - (Dynamics/ Body/ Physics)

Hey,
if I want to replace my sceene I got a crash via EXC BAD ACCESS… :confounded:
Director::getInstance()->replaceScene(myWorld_blanco::create());

At this line/file the crash stopped (external-Box2d-Dynamics-b2Fixture.cpp):

// Free the proxy array.
int32 childCount = m_shape->GetChildCount();

void b2Fixture::Destroy(b2BlockAllocator* allocator)
{
	// The proxies must be destroyed before calling this.
	b2Assert(m_proxyCount == 0);

	// Free the proxy array.
	int32 childCount = m_shape->GetChildCount();
	allocator->Free(m_proxies, childCount * sizeof(b2FixtureProxy));
	m_proxies = NULL;

	// Free the child shape.
	switch (m_shape->m_type)
	{
	case b2Shape::e_circle:
		{
			b2CircleShape* s = (b2CircleShape*)m_shape;
			s->~b2CircleShape();
			allocator->Free(s, sizeof(b2CircleShape));
		}
		break;

	case b2Shape::e_edge:
		{
			b2EdgeShape* s = (b2EdgeShape*)m_shape;
			s->~b2EdgeShape();
			allocator->Free(s, sizeof(b2EdgeShape));
		}
		break;

	case b2Shape::e_polygon:
		{
			b2PolygonShape* s = (b2PolygonShape*)m_shape;
			s->~b2PolygonShape();
			allocator->Free(s, sizeof(b2PolygonShape));
		}
		break;

	case b2Shape::e_chain:
		{
			b2ChainShape* s = (b2ChainShape*)m_shape;
			s->~b2ChainShape();
			allocator->Free(s, sizeof(b2ChainShape));
		}
		break;

	default:
		b2Assert(false);
		break;
	}

	m_shape = NULL;
}

If I follow the the crash-log I see, that the crash happends inside a subclass of my level - this subclass has one node with physic/dynamic. The node is calling a method after a collision with another sprite…

notification_subclass::~notification_subclass() {

}//<- EXC_BAD_ACCESS is here

It seems that Cocos2d-x wants to delete this body - but it is already deleted, right? So EXC_BAD_ACCESS is the result because it is NULL, or?
The problem is, that I got no reference “which” method inside my level or wich sprite/node is the trigger. I only guess, that it must be a note/dynamic because there is the crash in the b2Fixture.cpp file…

What is missing? Thanks for help.

I solved it with this line: ret->retain(); at the beginning of my subclass…

notification_subclass* notification_subclass::nodeWithDictionary(LHDictionary* dict, Node* prnt)
{
    notification_subclass *ret = new notification_subclass();
    if (ret && ret->initWithDictionary(dict, prnt))
    {
        ret->retain(); //this line was missing I found this solution inside the cocos2d-x code inside the comments.
        ret->autorelease();
        return ret;
    }
    else
    {
        CC_SAFE_DELETE(ret);
        return nullptr;
    }
}

Are you also manually calling release on the notification_subclass later on? Otherwise this is not really a solution, the only thing you are doing is creating a memory leak. The retaining only prevents a crash because it causes the deconstructor of notification_subclass never getting called in the first place.

I only use ret->autorelease(); at the next line… like this the example frome here: Correct usage (1):

I found this solutution at the comments inside cocos2d-x code… where my crash ends…

File: CCRef.cpp

void Ref::release()
{
    CCASSERT(_referenceCount > 0, "reference count should be 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_REF_LEAK_DETECTION
        untrackRef(this);
#endif
        delete this;
    }
}

Is this the right way?
or should I use ->cleanup?

notification_subclass::~notification_subclass(){

notification_subclassHandler->release();
}

The correct usage (1) part is just an example on how you should call retain on an object in case you want to manually autorelease it an additional time, outside of the create() function. You don’t need to do that, it’s completely irrelevant to your problem.

When creating an object you should just call ret->autorelease() like you did before, nothing more. After that it gets automatically retained when you add the node to a parent and released when you remove it. I recommend reading this article, it’s very helpful.

What you need to do instead is finding out why there is a bad access happening inside b2Fixture::Destroy. Unfortunately from the information given I can’t really say why it is happening.

hi,

this is exact my problem I cant figure out where the problem starts at real… If I read the crashlog with all treads there is no NODE or something else to get the idea “oh yes this is the point of my error”

I work with Levelhelper2 PRO - LH2 is retaining/realining all objects on its own (normally)…
I think that it is a node, because only in this subclass (I have several classes) exists a node with a physics-body (as dynamic) for a collision with another node…

Hm, I don’t have any experience with Levelhelper2 (didn’t even know this tool existed until now). Maybe someone else can help you out, or you reproduce the crash within a small project and I’ll take a look at it.

thank you :wink:

I will do “try and error” for today…

Hi @domp,

now I tested this before I call replaceScene. I have several subclasses, it make sense to remove them befor I init the new scene, right?

                          {// my code ...

                            notificationCoinsAssetHandler->removeFromParent();
                            notificationDiamondsAssetHandler->removeFromParent();
                            HUDlayerHandler->removeFromParent();
                            storeCoinsAssetHandler->removeFromParent();
                            storeDiamondsAssetHandler->removeFromParent();
                            storeLanguageAssetHandler->removeFromParent();
                            userSettingsHandler->removeFromParent();
                            notificationUnlockAssetHandler->removeFromParent();
                 
                            this->goTo(); (replaceScene)

                            // ...

                             }

Yes, seems like a valid solution if it fixes the bad access.

1 Like