Exc 访问错误

Posted

技术标签:

【中文标题】Exc 访问错误【英文标题】:Exc access error 【发布时间】:2013-08-04 22:24:24 【问题描述】:

C++ 新手,返回向量时遇到问题。我放了一个断点,数组是正确的(填充了我期望从查询中得到的所有对象)。但是当它返回时,我得到一个错误: EXC_BAD_ACCESS 在线 m_pComponentContainer->removeAll(); 来自 CCNode.cpp

这很奇怪,因为这是一个基类(不从任何类型的 CC 对象继承)虽然我广泛使用 Cocos2dx 框架,但它不包含在此类中。

我很确定这是因为某些东西正在被释放。但是,就像我说的,我对 C++ 很陌生,并且不确定问题出在哪里。在我不得不开始担心内存管理之前,我希望能在开发中走得更远一些。

 int numberOfCards = DatabaseHelper::getNumberOfCards();

//cant be zero
assert(numberOfCards);

std::vector<CardSlot> returnArray(numberOfCards);

sqlite3_stmt * statement;

if (sqlite3_open(this->dbpath.c_str(),&this->cardWarsDB) == SQLITE_OK)

    const char* query_stmt = "select ID, HP, MP, AbilityText from Cards WHERE ID IN (SELECT DISTINCT cardsID FROM Deck WHERE name = 'All')";

    if (sqlite3_prepare_v2(this->cardWarsDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
    
        while (sqlite3_step(statement) == SQLITE_ROW)
        
            CardSlot *aCard;

            const char* cardID = (const char*)sqlite3_column_text(statement, 0);
            const char* cardHP = (const char*)sqlite3_column_text(statement, 1);
            const char* cardMP = (const char*)sqlite3_column_text(statement, 2);
            const char* cardAbility = (const char*)sqlite3_column_text(statement, 3);

            if (cardID != NULL) 
                std::string imageName = ".png";
                imageName = cardID + imageName;
                aCard = (CardSlot *)CardSlot::spriteWithFile(imageName.c_str());
            

            if (cardID != NULL) 
                aCard->cardID = std::string(cardID);
                cocos2d::CCLog("DB returned results, cardID: %s",aCard->cardID.c_str());
            
            if (cardHP != NULL) 
                aCard->cardHP = std::string(cardHP);
                cocos2d::CCLog("DB returned results, cardHP: %s",aCard->cardHP.c_str());
            
            if (cardMP != NULL) 
                aCard->cardMP = std::string(cardMP);
                cocos2d::CCLog("DB returned results, cardMP: %s",aCard->cardMP.c_str());
            
            if (cardAbility != NULL) 
                aCard->cardAbility = std::string(cardAbility);
                cocos2d::CCLog("DB returned results, cardAbility: %s",aCard->cardAbility.c_str());
            

            numberOfCards--;

            returnArray[numberOfCards] = *aCard;

        
        sqlite3_finalize(statement);
    
    sqlite3_close(this->cardWarsDB);
    return returnArray;

这是堆栈跟踪的屏幕截图。我只是看着它,似乎是 CardSlot 对象是罪魁祸首。 但是还是不知道如何“保留”它们,不过我会看一些 Cocos 文档。

NOTE1

【问题讨论】:

也许 sqlite_step 迭代次数超过 numberOfCards 次?你可以在那里做一个简单的检查,例如:if(numberOfCards-- == 0) reportError(); 不:(它运行了五次。它达到零,但这是设计使然(对于 0 索引) 好的,但是您是在增量之前还是之后检查值?如果条件(numberOfCards-- == 0) 为真,那么您遇到了访问冲突。 您的堆栈跟踪显示了CardSlot 的析构函数。这是需要您注意的,还有复制构造函数和赋值运算符。 这一行:aCard = * CardSlot::spriteWithFile(imageName.c_str());,看起来非常非常可疑。 【参考方案1】:

您的CardSlot 似乎无法安全复制。你至少在两个地方复制CardSlots:

aCard = * CardSlot::spriteWithFile(imageName.c_str());(假设spriteWithFile返回CardSlot *,也是内存泄漏;“临时”没有被破坏) returnArray[numberOfCards] = aCard;

据我所知,您可能在CardSlot 中保留了一个CCSprite 指针,并在您的CardSlot 析构函数中销毁它(使用delete)。但是,由于副本的原因,该指针会被多次销毁,从而导致崩溃。

您需要重新设计您的类,以便可以安全地复制它,或者重构您的代码以便不复制(例如,使用vector&lt;shared_ptr&lt;CardSlot&gt; &gt; 来保存指向实例的指针)。

【讨论】:

我一直在调查你所说的,几个问题。首选方法是什么?在我看来,指针向量很好,但我应该担心我的类不能被复制吗?我没有任何析构函数,尽管我认为 CCNode(CardSlot 的父级)确实如此,这就是所谓的。让我感到困惑的是,如果它仍然被向量引用,为什么它会被解构。我还让 aCard 成为一个指针而不是一个实例化的对象,它应该可以正确地消除泄漏(检查上面的编辑)? @owengerig:C++ 不做引用计数。 CardSlot 的每个实例都是独立的:将CardSlot a,b; a = b; 复制 b 完全写入a 不,我知道 C++ 中没有引用计数器,但我使用的大部分术语来自 obj-c :(。如果我们重新审视“什么是首选方法”,在我看来,我不想创建副本,应该使用指针。【参考方案2】:

我已编辑代码以使用更多指针,而不是四处传递并用对象填充我的数组。然而,我认为有助于解决它的一件大事是使用cocos2d::CCArray 而不是std::vector. 我的大多数类都是 Cocos2d 类(CCSprites 和 CCLayers)的子类,因此使用它自己的数组数据类型是有意义的。

cocos2d::CCArray DatabaseHelper::getAllCards()

    int numberOfCards = DatabaseHelper::getNumberOfCards();

    //cant be zero
    assert(numberOfCards);

    cocos2d::CCArray returnArray(numberOfCards);

    sqlite3_stmt * statement;

    if (sqlite3_open(this->dbpath.c_str(),&this->cardWarsDB) == SQLITE_OK)
    
        const char* query_stmt = "select ID, HP, MP, AbilityText from Cards WHERE ID IN (SELECT DISTINCT cardsID FROM Deck WHERE name = 'All')";

        if (sqlite3_prepare_v2(this->cardWarsDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
        
            while (sqlite3_step(statement) == SQLITE_ROW)
            
                CardSlot* aCard;

                const char* cardID = (const char*)sqlite3_column_text(statement, 0);
                const char* cardHP = (const char*)sqlite3_column_text(statement, 1);
                const char* cardMP = (const char*)sqlite3_column_text(statement, 2);
                const char* cardAbility = (const char*)sqlite3_column_text(statement, 3);

                if (cardID != NULL) 
                    std::string imageName = ".png";
                    imageName = cardID + imageName;
                    aCard = CardSlot::spriteWithFile(imageName.c_str());
                

                if (cardID != NULL) 
                    aCard->cardID = std::string(cardID);
                    cocos2d::CCLog("DB returned results, cardID: %s",aCard->cardID.c_str());
                
                if (cardHP != NULL) 
                    aCard->cardHP = std::string(cardHP);
                    cocos2d::CCLog("DB returned results, cardHP: %s",aCard->cardHP.c_str());
                
                if (cardMP != NULL) 
                    aCard->cardMP = std::string(cardMP);
                    cocos2d::CCLog("DB returned results, cardMP: %s",aCard->cardMP.c_str());
                
                if (cardAbility != NULL) 
                    aCard->cardAbility = std::string(cardAbility);
                    cocos2d::CCLog("DB returned results, cardAbility: %s",aCard->cardAbility.c_str());
                

                numberOfCards--;

                returnArray.addObject(aCard);

            
            sqlite3_finalize(statement);
        
        sqlite3_close(this->cardWarsDB);
        return returnArray;
    

    //incase sql fails, close db and created a "FAILED" card
    sqlite3_close(this->cardWarsDB);
    cocos2d::CCLog("DB returned error: cant open char catagories file");
    cocos2d::CCArray failedReturnArray(1);
    CardSlot * aCard;
    aCard->cardID = std::string("FAILED");
    aCard->cardHP = std::string("FAILED");
    aCard->cardMP = std::string("FAILED");
    aCard->cardAbility = std::string("FAILED");
    failedReturnArray.addObject(aCard);
    return failedReturnArray;

如果有人关心这里是 CardSlot (不多,此时只构建了构造函数):

CardSlot * CardSlot::spriteWithFile(const char *pszFileName)


    CCLOG("CardSlot::spriteWithFile");
    CardSlot * aCard = new CardSlot();
    if (aCard && aCard->initWithFile(pszFileName))
    
        aCard->cardID = pszFileName;
        aCard->scheduleUpdate();
        aCard->autorelease();
        return aCard;
    
    CC_SAFE_DELETE(aCard);
    return NULL;

我唯一关心的是我认为我的 CCArray 应该是一个指针。但是它现在可以工作,并且学习所有内存管理“交易技巧”会及时出现,我使用 C++ 的工作越多

感谢@nneonneo 的所有帮助我确信您的修复会奏效,我尝试了,但无论我做了什么都无法让矢量工作。我尽可能多地支持你,但实际上这是我实施的“答案”。

【讨论】:

【参考方案3】:

returnArray 被声明为函数的局部变量,当函数返回时它会被释放。您需要将其声明为 static 或将声明移至函数外部。

【讨论】:

按值返回vector 对象是完全合法的。

以上是关于Exc 访问错误的主要内容,如果未能解决你的问题,请参考以下文章

如何删除此 EXC_BAD 访问错误?

iphone sdk 值从给定 exc 错误访问的数组中删除

Swift 在访问 NSManagedObject 的属性时给出错误线程 1:EXC_BAD_ACCESS(code=1, address=0x0)

无法访问 CoreData

Crash类型

AVAudioPlayer EXC_BAD_ACCESS