0

我正在开发我的第一个大型 Objective-c 应用程序,一个纸牌游戏。由于各种原因,我选择使用包含整数、布尔值、数组和各种指针的 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 对象也有一个类方法来创建一个新的游戏实例。这是它的代码:

+(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 都是在堆上分配内存。

我知道当我完成它时,我需要为这个 gameState 释放内存。

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

我假设一旦从 newGameStateWithNumberOfPiles:andNumberOfCards: 返回了 gameState,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 并将其返回给我的 viewController,它会保留该引用。当用户开始另一个游戏时,我首先调用 [MDGameState freeGameState:gameState recurse:YES] 来完全清理之前的 gameState,然后我使用 [MDGameState newGameStateWithNumberOfPiles:13 andNumberOfCards:52] 创建一个全新的 gameState。

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

你有什么想法吗?谢谢你的帮助!

4

1 回答 1

1

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

您的方法中有泄漏,+freeGameState:recurse:因为您从不调用 free(gameState),这意味着每个 gameState 都已泄漏。

没错,MDGameState 类在从+newGameStateWithNumberOfPiles:andNumberOfCards.

是的,gameState 的调用者newGameStateWithNumberOfPiles:andNumberOfCards负责释放内存。

于 2013-02-16T22:43:23.447 回答