Я работаю над своим первым большим приложением на Objective-C, карточной игрой. По разным причинам я решил поддерживать состояние моей игры с помощью структуры C, которая содержит целые числа, логические значения, массивы и различные указатели. (Для любопытства, я делаю это, потому что в конечном итоге мне придется создавать и уничтожать тысячи и тысячи этих gameStates, и я хотел быть как можно ближе к «голому железу».) Структура gameState определяется следующим образом:
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: все mallocs выделение памяти в куче.
Я знаю, что мне нужно освободить память для этого состояния игры, когда я закончу с этим.
Мне интересно, отслеживает ли 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 и возвращается в мой viewController, который сохраняет эту ссылку. Когда пользователь запускает другую игру, я сначала вызываю [MDGameState freeGameState:gameState recurse:YES], чтобы полностью очистить предыдущее состояние gameState, а затем я создаю совершенно новое состояние игры, используя [MDGameState newGameStateWithNumberOfPiles:13 andNumberOfCards:52].
Я думаю, что правильно убираю за собой. Однако инструменты говорят мне, что у меня есть утечки памяти. Он специально выделяет выделенную жирным шрифтом строку в newGameStateWithNumberOfPiles:andNumberOfCards: пример выше. Это происходит почти сразу после того, как я начинаю новую игру. Он также выделяет идентичный код в функции, которая создает копию gameState. Я не понимаю, почему этот malloc выделен, а два над ним нет. Если я изменю порядок malloc в этой функции, он все равно выделит malloc для карт.
Есть шанс, что у вас есть какие-нибудь мысли по этому поводу? Спасибо за вашу помощь!