alfonsocejudo / EasyNDK-for-cocos2dx3

An update to the original EasyNDK project for use with Cocos2d-x v3.0 and (hopefully) higher.
28 stars 14 forks source link

Problem when change scene - Freed objects #2

Closed paulocoutinhox closed 10 years ago

paulocoutinhox commented 10 years ago

Hi,

Im trying use it but i have problems when change the scene (Transition), on destructor i remove the selectors from current scene and get exception.

My MenuScene init code:

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

NDKHelper::addSelector("MenuSceneSelectors", "onFacebookLoginOK", CC_CALLBACK_2(MenuScene::onFacebookLoginOK, this), this);
NDKHelper::addSelector("MenuSceneSelectors", "onFacebookLoginError", CC_CALLBACK_2(MenuScene::onFacebookLoginError, this), this);

#endif

My MenuScene destructor code:

MenuScene::~MenuScene()
{
    NDKHelper::removeSelectorsInGroup("MenuSceneSelectors");    
}

After user goto stage select scene with code:

void MenuScene::menuStartGameCallback(Ref *sender)
{
    auto director = Director::getInstance();

    TransitionFade *trans = TransitionFade::create(GameObjects::sceneTransitionSpeed, StageSelectScene::createScene(), Color3B::WHITE);
    director->replaceScene(trans);
}

I got error:

XXX iOS(39833,0x397a1a8) malloc: *** error for object 0x9f39bb4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
XXX iOS(39833,0x397a1a8) malloc: *** error for object 0xaa2f9f4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug

I think that on your method:

void NDKHelper::removeAtIndex(int index)
{
    NDKHelper::selectorList[index] = NDKHelper::selectorList.back();
    NDKHelper::selectorList.pop_back();
}

When you call "pop_back", it is trying to free a object that already was removed from memory or something like this.

Can you help me?

aajiwani commented 10 years ago

http://www.cplusplus.com/reference/vector/vector/pop_back/

pop_back actually removes the last object from the container and destroys it.

paulocoutinhox commented 10 years ago

No solution? The object are from cocos2d, the scene is a cocos2d::Layer and the selector make references to my scene methods.

aajiwani commented 10 years ago

Let me just check the issue out. Sorry for the inconvenience.

paulocoutinhox commented 10 years ago

Bah. No problem man. Im here to help.

paulocoutinhox commented 10 years ago

The problem is when it run the action in "handleMessage":

void NDKHelper::handleMessage(json_t *methodName, json_t *methodParams)
....
target->runAction(action);
....

Others screenshots to try help:

aajiwani commented 10 years ago

Hello prosolucoes,

Can you please make sure that you have completed all the steps mentioned here.

http://cocos2d-x.org/wiki/How_to_work_on_iOS_-_C++Objective-C

because if the requirements aren't met, they might not work as required.

Thanks.

paulocoutinhox commented 10 years ago

Yes man. It is working on android and iOS. The problem is when you Remove Selectors or destroy the scene that is attached to it.

You dont problem with this sample becausr only have one Scene. Try create another and change to other Scene and back.

alfonsocejudo commented 10 years ago

Hi prosolucoes,

So you've tried taking the EasyNDKSample yourself and added another scene with a transition, and it doesn't work?

paulocoutinhox commented 10 years ago

Hi,

You are correct. My projects has any bug that is causing it.

I have added a new Scene on your demo project and it works nice i go to scene2 and back too many times and it works as spected.

Sorry man, i will check it here.

alfonsocejudo commented 10 years ago

Hey, let us know what you find. If your project isn't doing anything out of the ordinary, it might be the case that EasyNDK should be supporting some use case that it currently does not. Good luck debugging.

paulocoutinhox commented 10 years ago

Hi man. Can you try help me? I make a lot of tests using my project and your project. In my project i get an error and on your project with exact the same code, i dont get error. The error on my project only happen after the next scene init.

The error:

iOS(2216,0x3b98818c) malloc: ***
error for object 0x8a4334: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug

The steps when debug: 1 - Im on MenuScene 2 - I click on a button and it call a method from NDKHelper 3 - It call a objc method and objc code call a native C++ method 4 - The callback is a function that make a transition to other scene called StageScene (no problem here...everything is working) 5 - After StageScene::init, i get that error ^ ^

Image: https://www.dropbox.com/s/g7smp7zy6b7so67/Screenshot%202014-03-27%2020.03.24%282%29.png

If you have skype, can you add me? paulo.prsolucoes

paulocoutinhox commented 10 years ago

I have tested here another think more simple. If i add one or more selector using "NDKHelper::addSelector" and only change the SCENE i got the problem on image. My code:

bool MenuScene::init()
{
    if ( !Layer::init() )
    {
        return false;
    }

    ...

    // ndk
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

    NDKHelper::addSelector("MenuSceneSelectors", "onFacebookLoginOK", CC_CALLBACK_2(MenuScene::onFacebookLoginOK, this), this);
    NDKHelper::addSelector("MenuSceneSelectors", "onFacebookLoginError", CC_CALLBACK_2(MenuScene::onFacebookLoginError, this), this);

    #endif

    return true;
}
MenuScene::~MenuScene()
{
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    NDKHelper::removeSelectorsInGroup("MenuSceneSelectors");
    #endif
}

Me method has facebook on name, but dont have facebook integration code, it is only a test to after include facebook files and code. It is a simple method only.

paulocoutinhox commented 10 years ago

More one thing that i discover now. The problem only happen, when i use two selectors with the same Group Name, if i change the Group Name, it dont happen. I change my code to have Group Name different and remove the two groupds, and it is working now. o.o

On init:

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

    NDKHelper::addSelector("MenuSceneSelectors1", "onFacebookLoginOK", CC_CALLBACK_2(MenuScene::onFacebookLoginOK, this), this);
    NDKHelper::addSelector("MenuSceneSelectors2", "onFacebookLoginError", CC_CALLBACK_2(MenuScene::onFacebookLoginError, this), this);

#endif

On destructor:

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

    NDKHelper::removeSelectorsInGroup("MenuSceneSelectors1");
    NDKHelper::removeSelectorsInGroup("MenuSceneSelectors2");

#endif

It is so strange :(

paulocoutinhox commented 10 years ago

I think that i found the problem. The list to be removed is removing the same object two times, and on the second time it is crashing. I make a screenshot section to you see:

1 - The list with all items, unmodified: https://www.dropbox.com/s/tx7hys5mjvc8zkh/Screenshot%202014-03-27%2020.35.59%282%29.png

2 - The list with items after put on index 0 the last item: https://www.dropbox.com/s/icyf2um7g9tcchn/Screenshot%202014-03-27%2020.38.08%282%29.png

\ The pointer address here is: 0x865c68 - it will be removed now **

3 - The onFacebookLoginError was removed, OK. So we will remove now the second item: https://www.dropbox.com/s/asj7d9kmm5gluxq/Screenshot%202014-03-27%2020.39.40%282%29.png

\ The pointer address here is the same as previous: 0x865c68 - it will be removed now AGAIN **

4 - After pop_back this item again. I got that error :(

iQD commented 10 years ago

Dunno if you still have troubles, but I just came across your post.

It's not really strange. It is what the implementation of removeSelectorsInGroup does. It has a major programming error. It's basically invalidating the indexes of the vector container.

NDKHelper::removeSelectorsInGroup("MenuSceneSelectors1");
NDKHelper::removeSelectorsInGroup("MenuSceneSelectors2");

This just works, cause it does not invalidate the indexes.

What removeSelectorsInGroup does is the following: The vector has the elements: ["MenuSceneSelectors1", "MenuSceneSelectors2"] MenuSceneSelectors1 is found at index 0 and calls removeAtIndex(0)

void NDKHelper::removeAtIndex(int index)
{
    NDKHelper::selectorList[index] = NDKHelper::selectorList.back();
    NDKHelper::selectorList.pop_back();
}

This is altering the vector as following: It changes the object at index 0 to the last one(it is not really deleting anything): The Vector has now the elements: ["MenuSceneSelectors2", "MenuSceneSelectors2"]

After that, it is popping the last element: The Vector has now the elements: ["MenuSceneSelectors2"]

Now, the second call of removeSelectorsInGroup does the following: MenuSceneSelectors2 is found at index 0 and calls removeAtIndex(0). It does not crash, as both calls have 0 as index.

Now we look at the crash: NDKHelper::removeSelectorsInGroup("MenuSceneSelectors");

The vector has the elements: ["MenuSceneSelectors", "MenuSceneSelectors"] MenuSceneSelectors is found at index 0 and index 1 and calls removeAtIndex(0) and calls removeAtIndex(1) afterwards:

1st call with index 0: It changes the object at index 0 as the last one(it is not really deleting anything): The Vector has now the elements: ["MenuSceneSelectors", "MenuSceneSelectors"]

After that, it is popping the last element: The Vector has now the elements: ["MenuSceneSelectors"]

2nd call with index 1: It changes the object at index 1 to the last one(it is not really deleting anything): The Vector has the elements: ["MenuSceneSelectors"] As you can see, there is no element at index 1, as the first call was invalidating the index from 1 to 0 but popped the element at index 1. Dereferencing an index of a vector container, which is out of bounds, is undefined behavior. And that is why you get the error: object 0x8a4334: incorrect checksum for freed object - object was probably modified after being freed.

The program does not crash at the popping of the last element, it crashes cause it is trying o modify an element, that was freed before(hence the error message).

Look at your provided screenshot: https://www.dropbox.com/s/asj7d9kmm5gluxq/Screenshot%202014-03-27%2020.39.40%282%29.png

It tries to access element/index 1 of a vector which is only size 1 -> crash.

** The pointer address here is: 0x865c68 - it will be removed now **

Not really. Not 0x865c68 will be removed but 0x865ca0 and the second call with index 1 tries to access the element at 0x865ca0.

pop_back() does not really remove an element of the vector container, it just invalidates it. It is still there, hence it can tell you, that you tried to access the element at index 1, which was marked as being freed.

Possible solution: Change the code of the removeAtIndex using the appropriate functions:

void NDKHelper::removeAtIndex(int index)
{
    NDKHelper::selectorList.erase(NDKHelper::selectorList.begin() + index);
}

Some suggestions to the author of EasyNDK: Never use the [] operator using a vector container when writing portable code. Always use the at() function, as it will do bounds checking, which [] does not and accessing an index out of bounds is undefined behavior.

Why did you use "marking" and "popping" instead of the erase() function? Too slow? If so, use the appropriate container for your problem.

vector is not even the appropriate container for such a problem. You don't need random access in the container. You are doing linear search for the group string anyway and could delete the found objects right in place. No need for marking indexes or using a second for loop.

Use a list or a multiset and implement the () operator for your group string comparison and utilize remove()/find()/equal_range() and erase() on the related container. Or even use a map. A map is just what reflects your situation: grouping together objects, where you can delete all objects matching your key(the groupName) at once.

Tl;dr: It is a major programming error compared with the usage of an inappropriate container.

HTH

alfonsocejudo commented 10 years ago

Thanks iQD. I actually had my own fix in my game some time ago after I came across the same issues (the crash doesn't manifest unless you have more than 2 selectors in a scene). I've been neglecting this repo until I published my game, and now that it's published, I've pushed my changes and hopefully it works for everyone (only been tested on cocos2d-x v3.0 final for iOS and Android).