c - 关于在 Objective-c 堆上分配 c 结构和数组所需的指南

标签 c objective-c memory-leaks malloc heap-memory

我正在开发我的第一个大型 Objective-c 应用程序,一个纸牌游戏。由于各种原因,我选择使用包含整数、 bool 值、数组和各种指针的 C 结构来维护游戏状态。 (出于好奇,我这样做是因为我最终必须创建和销毁成千上万个这样的游戏状态,并且我希望尽可能接近“裸机”。)游戏状态结构定义如下:

struct GameState {
    // changing game state properties
    int score;
    int moves;
    bool won;

    int numberOfCards;
    int numberOfPiles;

    // doneness of piles
    bool *pileDoneness;
    bool *pileDisabled;

    // array of cards (each item is a pointer to a card)
    Card *cards;

    // defining piles (each card in the pile is a pointer to a pointer Card in the cards array)
    Card ***piles;

    // previous gameStates
    struct GameState *previous;
};

需要注意的是,在游戏中进行移动之前,我会复制 gameState(除前一个值之外的所有值),并将 gameState->previous 设置为副本。

一张牌只是一个整数,其中不同的字节代表不同的牌属性(花色、面朝上/面朝下、面值等)。

我还有一个 MDCard 类和 MDGameState 类。这两个类都只有类方法,我从不创建其中任何一个的实例。它们具有可以分别创建和操作卡片和游戏状态的方法。为了解释一下,您可以使用以下代码翻转卡片:

// assume card already exists
[MDCard flip:card];

我可以获得这样的卡片颜色:

Color color = [MDCard color:card];

好的,好的。现在 MDGameState 对象还有一个类方法来创建新的 gameInstance。这是它的代码:

+(GameState *)newGameStateWithNumberOfPiles:(u_int)numberOfPiles andNumberOfCards:(u_int)numberOfCards{
    // create our new gameState struct
    GameState *gameState = malloc(sizeof(GameState));

    // array of state of doneness of piles
    gameState->pileDoneness = malloc(sizeof(bool) * numberOfPiles);
    for (int i = 0 ; i < numberOfPiles ; i++) gameState->pileDoneness[i] = false;

    gameState->pileDisabled = malloc(sizeof(bool) * numberOfPiles);
    for (int i = 0 ; i < numberOfPiles ; i++) gameState->pileDisabled[i] = false;

    // array of pointers to cards
    **gameState->cards = malloc(sizeof(Card) * numberOfCards);**
    for (int i = 0 ; i < numberOfCards ; i++) gameState->cards[i] = 0;

    // array of array of pointers to Cards in cards array
    gameState->piles = malloc(sizeof(Card **) * numberOfPiles);
    for (int p = 0 ; p < numberOfPiles ; p++){
        gameState->piles[p] = malloc(sizeof(Card *) * numberOfCards);
        for(int c = 0 ; c < numberOfCards ; c++){
            gameState->piles[p][c] = nil;
        }
    }

    gameState->moves = 0;
    gameState->score = 0;
    gameState->won = false;

    // piles
    gameState->numberOfPiles = numberOfPiles;
    gameState->numberOfCards = numberOfCards;
    gameState->previous = nil;

    return gameState;
}

我目前的理解是gameState将在堆中分配。实际上,gameState 变量只是指向堆中分配的内存的指针。堆中分配的内存包含各种值,例如移动次数、得分、获胜等。它还包含指向堆中其他分配的内存的指针,用于纸牌、桩等的 gameState 数组。IE:所有 malloc 都是在堆上分配内存。

我知道当我完成这个游戏状态时,我需要释放它的内存。

我想知道,Objective-C ARC 是否跟踪对此 gameState 结构的引用?它引用的数组怎么样?我目前认为答案是否定的。

我假设一旦 gameState 从 newGameStateWithNumberOfPiles:andNumberOfCards: 返回,MDGameState 类就不再有对它的引用。它是否正确?

我假设最终引用 gameState 的对象负责在完成 gameState 后释放其内存?

只要用户在玩游戏,我就需要 gameState 对象一直存在。再次注意,每次用户进行移动时,都会创建 gameState 的副本并将其设置为前一个 gameState。这是为了撤消目的。

我在 MDGameState 上创建了一个函数,该函数应该释放该结构使用的所有内存:

+ (void)freeGameState:(GameState *)gameState recurse:(BOOL)recurse {
    if(recurse && gameState->previous != nil){
        [MDGameState freeGameState:gameState->previous recurse:recurse];
    }

    for (int p = 0 ; p < gameState->numberOfPiles ; p++){
        free(gameState->piles[p]);
    }

    free(gameState->piles);
    free(gameState->pileDoneness);
    free(gameState->pileDisabled);
    free(gameState->cards);
}

请注意,在某些情况下,我可能想要取消分配当前的 gameState,而不是之前的状态。例如,如果我撤消一个 Action 。

因此,当用户开始新游戏时,将创建 gameState 并将其返回到我的 viewController,该 viewController 会保留该引用。当用户开始另一个游戏时,我首先调用 [MDGameState freeGameState:gameState recurse:YES] 来完全清理之前的 gameState,然后使用 [MDGameState newGameStateWithNumberOfPiles:13 andNumberOfCards:52] 创建一个全新的 gameState。

我认为我正在妥善清理自己。但是,Instruments 告诉我存在内存泄漏。它特别突出显示了上面示例中 newGameStateWithNumberOfPiles:andNumberOfCards: 中的粗体行。几乎在我开始新游戏时就会发生这种情况。它还突出显示了复制 gameState 的函数中的相同代码。我不明白为什么 malloc 被突出显示,而上面的两个则没有突出显示。如果我更改该函数中 malloc 的顺序,它仍然会突出显示卡的 malloc。

您对此有什么想法吗?感谢您的帮助!

最佳答案

你是对的,ARC不管理用malloc()分配的内存。

+freeGameState:recurse: 方法中存在泄漏,因为您从未调用 free(gameState),这意味着每个 gameState 都被泄漏。

正确,从 +newGameStateWithNumberOfPiles:andNumberOfCards 返回后,MDGameState 类将不会引用 gameState。

是的,newGameStateWithNumberOfPiles:andNumberOfCards 的调用者负责在游戏状态完成后释放内存。

关于c - 关于在 Objective-c 堆上分配 c 结构和数组所需的指南,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14915842/

相关文章:

c - 如何将 typedef 结构与实例创建结合起来?

c - 这个C程序是如何工作的?

objective-c - 当前模态和解除 View Controller 无法正常工作

c++ - 查找数组之间的内存泄漏

JavaScript 导致大量内存泄漏

在 OSX 上为 Shewchuk 的三角程序编译 C 浮点运算

c - 防止编译器优化静态结构变量

objective-c - 嵌套单引号

objective-c - 如何在 ObjectiveC 类中存储函数指针?

c# - 压缩图像时 C# 中的内存泄漏