CCUserDefault flush: causing crash in CFPreferences Background Sync

Hi,
We developed a game using cocos2dx framework. Our app crashed in following line (not all the time, around 1 in 20 times). Can you please help us to identify the issue.
CCUserDefault::sharedUserDefault()->setIntegerForKey(“NetWorth”, netWorth); CCUserDefault::sharedUserDefault()->flush();

crash log: Crashed: CFPreferences Background Sync Queue EXC_BAD_ACCESS UNKNOWN at 0x006a5936 raw
0 CoreFoundation __CFTypeCollectionRetain + 7
1CoreFoundation _CFArrayReplaceValues + 260
2 CoreFoundation _CFArrayReplaceValues + 260

OilTycoon
GamePlayScene.cpp line 11525 GamePlayScene::getNetWorth()

Please post the code of GamePlayScene::getNetWorth(), as it is the code, which triggers the crash.

long int GamePlayScene::getNetWorth()
{
// Cash On Hand
int cashOnHand = var_ImportExport.var_cash;

// TotalLandValue
int totalLandValue = 0;

// Getting Total LandValue
CCArray * RowidArray = CCArray::createWithArray(m_landPlotObject->getPurchasedPlotList());
RowidArray->retain();

for (int i = 0; i < RowidArray->count(); i++)
{
    
    CCString * temp = ((CCString *)RowidArray->objectAtIndex(i));
    int rowid = convertToInteger(temp);
    totalLandValue += m_landPlotObject->getLandValueWithRowid(rowid);// TotalLandValue
}

int crudeOilValue = var_ImportExport.var_crude_in_stock * var_ImportExport.var_Export_price_per_gallon;
int dieselProductValue = var_refineryProduc.var_diesel_fuel_in_stock * var_refineryProduc.var_Diesel_fuel_market_price;
int asphaltProductValue = var_refineryProduc.var_Asphalt_fuel_in_stock * var_refineryProduc.var_Asphalt_fuel_market_price;
int gasolineProductValue = var_refineryProduc.var_Gasoline_fuel_in_stock * var_refineryProduc.var_Gasoline_fuel_market_price;
int propaneProductValue = var_refineryProduc.var_Propane_fuel_in_stock * var_refineryProduc.var_Propane_fuel_market_price;
int keroseneProductValue = var_refineryProduc.var_Kerosene_fuel_in_stock * var_refineryProduc.var_Kerosene_fuel_market_price;

// Total Product Value
long int totalProductValue = dieselProductValue + asphaltProductValue + gasolineProductValue + propaneProductValue + keroseneProductValue;

// Total Refinery Value
int totalRefineryValue =  var_buySellRefinery.var_Refinery_price * var_buySellRefinery.var_Refineries;

// Total Storage Value
int totalStorageValue = var_buySellStorageUnits.var_StorageUnits_price * var_buySellStorageUnits.var_storage_units;

// Total Debt

int TotalDebt = CCUserDefault::sharedUserDefault()->getIntegerForKey("TotalDebt");

// Calculating total Networth
long int netWorth = cashOnHand + totalLandValue +
crudeOilValue + totalProductValue + totalRefineryValue + totalStorageValue - TotalDebt;

var_applyLoan.var_net_worth = netWorth;
CCUserDefault::sharedUserDefault()->setIntegerForKey("NetWorth", netWorth);
CCUserDefault::sharedUserDefault()->flush();

return netWorth;

}

It is crashing in line above the last line.

You mean in this line:

CCUserDefault::sharedUserDefault()->flush();

Or the setIntegerForKey?

flush is just an empty method:

void UserDefault::flush()
{
}

You should step into the following function, and check if there is a problem with copying the value into the buffer or saving the value to the file in setValueForKey(pKey, tmp);:

void UserDefault::setIntegerForKey(const char* pKey, int value)
{
    // check key
    if (! pKey)
    {
        return;
    }

    // format the value
    char tmp[50];
    memset(tmp, 0, 50);
    sprintf(tmp, "%d", value);

    setValueForKey(pKey, tmp);
}

Try to find variable using the memory address, which will be printed out in the stacktrace:

Queue EXC_BAD_ACCESS UNKNOWN at 0x006a5936 

Print out the address of your netWorth variable and also look at the address used in setValueForKey(pKey, tmp);.

As your code crashed in 0 CoreFoundation __CFTypeCollectionRetain + 7, it is a sign, your variable is destroyed, before giving the CF code a chance to use it.

Does your code use threads for saving/reading to CCUserDefault?

Hi,

This is the code for setIntegerForKey function. netWorth is a local int variable. not sure why it could get release. we are not using separate thread. Entire app runs in one thread. Can you please let me know how I can make sure netWorth variable is not releases?

void CCUserDefault::setIntegerForKey(const char* pKey, int value)
{
#ifdef KEEP_COMPATABILITY
    deleteNodeByKey(pKey);
#endif

    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:value] forKey:[NSString stringWithUTF8String:pKey]];

}

You can make it persistent, if you are allocating it on the stack/heap and keep a reference on it, but this is not the problem. It’s just a local variable, as you stated.

It seems the compiler does not like networth to be a long int, but copying the value to an int.

long int is 8 bits and int is 4 bits on 64-bit systems. Assigning long int to int is compiler/undefined behavior.
Can you try to change it to an int and test it?

You should also breakpoint at [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:value] forKey:[NSString stringWithUTF8String:pKey]]; and see why it is crashing. See if the value is valid.