私は最初の大きなObjective-cアプリケーションであるカードゲームに取り組んでいます。さまざまな理由から、int、bool、配列、およびさまざまなポインターを含むC構造体を使用してゲームの状態を維持することを選択しました。(不思議なことに、私は最終的にこれらのgameStateを何千も作成して破棄する必要があり、「ベアメタル」にできるだけ近づけたいと思ったので、これを行っています。)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;
};
1つの注意点として、ゲームで移動が行われる直前に、gameState(前を除くすべての値)のコピーを作成し、gameState->previousをコピーに設定します。
カードは単なる整数であり、さまざまなバイトがさまざまなカードのプロパティ(スーツ、表向き/下向き、値など)を表します。
MDCardクラスとMDGameStateクラスもあります。これらのクラスは両方ともクラスメソッドしかなく、どちらのインスタンスも作成しません。それらには、それぞれカードとgameStatesを作成および操作できるメソッドがあります。説明すると、次のコードを使用してカードを裏返すことができます。
// 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はヒープにメモリを割り当てます。
使い終わったら、このgameStateのメモリの割り当てを解除する必要があることを知っています。
疑問に思っていますが、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の割り当てを解除したいが、以前の状態の割り当てを解除したい場合があることに注意してください。たとえば、移動を元に戻す場合。
したがって、ユーザーが新しいゲームを開始すると、gameStateが作成され、その参照を保持するviewControllerに返されます。ユーザーが別のゲームを開始するとき、最初に[MDGameState freeGameState:gameState recurse:YES]を呼び出して、前のgameStateを完全にクリーンアップしてから、[MDGameState newGameStateWithNumberOfPiles:13 andNumberOfCards:52]を使用してまったく新しいgameStateを作成します。
後片付けはきちんと片付けていると思います。しかし、Instrumentsは私にメモリリークがあると言っています。これは、上記の例のnewGameStateWithNumberOfPiles:andNumberOfCardsの太字の行を具体的に強調しています。これは、新しいゲームを開始するとすぐに発生します。また、gameStateのコピーを作成する関数内の同一のコードを強調表示します。そのmallocが強調表示されている理由と、その上の2つが強調表示されていない理由がわかりません。その関数でmallocの順序を変更しても、カードのmallocが強調表示されます。
これについて何か考えがある可能性はありますか?ご協力いただきありがとうございます!