2009-11-02 3 views
2

J'essaie de regarder à travers un NSMutableDictionary chargé avec un NSMutableArray et je suis en train de tout chambouler, et je ne sais pas comment. J'essaie de charger un plus grand nombre de questions de jeu, puis les supprimer si elles ne sont pas au bon niveau. Je n'obtiens pas d'erreur jusqu'à ce que j'essaie de supprimer. Quelqu'un peut-il voir la faille dans ce code? Je l'apprécierais super!NSMutable Array - objectAtIndex: index au-delà des limites

Merci,

~ G

{ 
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults]; 
NSString *GameLevel = [[NSString alloc] initWithFormat: [settings objectForKey:kLevelKey]]; 

NSBundle *Bundle = [NSBundle mainBundle]; 
NSString *PListPath = [Bundle pathForResource:@"questions" ofType:@"plist"]; 

NSMutableDictionary *Dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:PListPath]; 

self.QuestionDetailsByLevel = Dictionary; 
[Dictionary release]; 

NSMutableArray *Components = [[NSMutableArray alloc] initWithArray:[QuestionDetailsByLevel allKeys]]; 
self.QuestionsByLevel = Components; 

int QuestionCount = [self.QuestionsByLevel count] - 1; 

for (int j = 0; j < QuestionCount - 1; j++) 
{ 

    NSString *SelectedQuestion = [self.QuestionsByLevel objectAtIndex:j]; 
    NSMutableArray *Array = [QuestionDetailsByLevel objectForKey:SelectedQuestion]; 
    self.QDetailsByLevel = Array; 

    NSString *level = [[NSString alloc] initWithFormat:[self.QDetailsByLevel objectAtIndex:Level]]; 

    if (level != GameLevel) 
     [QuestionsByLevel removeObjectAtIndex:j]; 
} 
} 
+0

Y a-t-il une raison pour laquelle vous mettez tout en une seule fois? Ayez juste un pour chaque niveau, puis chargez celui dont vous avez besoin. –

Répondre

3

Le problème se produit, si je ne me trompe pas, en raison du fait que vous modifiez l'objet pendant que vous itérez son contenu. Lorsque vous supprimez des objets de la liste, votre condition de terminaison devient invalide. Essayez de l'implémenter en tant que boucle while et veillez à mettre à jour votre condition de terminaison chaque fois que vous supprimez des éléments de la liste. Vous pouvez également utiliser le débogueur pour savoir où vous étiez hors des limites.

9

Ceci n'est pas une réponse. C'est une critique de votre code.

  1. La mémoire fuit, Batman! Vous allou/init: GameLevel, Components et level, mais n'en libérez jamais aucun.
  2. GameLevel n'a pas besoin d'être alloc/init du tout. Vous pouvez simplement retirer la valeur de [settings objectForKey:kLevelKey];, l'affecter à votre chaîne GameLevel et l'utiliser. Ensuite, vous n'avez même pas à le libérer.
  3. Votre boucle est ... impaire. Vous itérez la boucle, mais chaque fois que vous l'itérez, vous définissez la propriété self.QDetailsByLevel sur une nouvelle valeur. Es-tu sûr que c'est ce que tu veux?
  4. Ceci: if (level != GameLevel) ne fait pas ce que vous pensez qu'il fait. C'est comparer les pointeurs (c'est-à-dire, les ADRESSES de deux objets en mémoire). Dans votre état actuel, et GameLevel ont été allou/init, ce qui signifie qu'ils ne seront jamais le même objet. Vous voulez probablement if ([level isEqualToString:GameLevel] == NO) à la place.
  5. Vous soustrayez-en un de [self.QuestionsByLevel count] pour obtenir votre QuestionCount int, ce qui semblerait être une borne supérieure d'une boucle for(). Cependant, le conditionnel de la boucle for (que @Michael a montré être votre problème) soustrait un autre 1 de QuestionCount, ce qui signifie que votre boucle for() n'atteindra jamais le dernier élément du tableau. Es-tu sûr que c'est ce que tu veux?
  6. Mémorisez ceci: http://www.cocoadevcentral.com/articles/000082.php (ou this)
+1

De même, ne laissez pas l'utilisateur fournir des chaînes de format. 'initWithFormat: [settings objectForKey ...]' est toujours incorrect. L'utilisateur * écrira * une chaîne de format valide, mais attend les arguments que vous ne lui donnez pas. Ensuite, votre application va planter. Toujours passer soit une chaîne constante ('@" ... "') ou une version localisée d'un ('NSLocalizedString (@" ... ",/* comment */@" Explication de la chaîne ")'. –

+1

Je m'interroge aussi sur la présence de deux propriétés, 'QuestionDetailsByLevel' et' QDetailsByLevel', de différents types: asUwish, vous devriez choisir l'un ou l'autre, ou donner un meilleur nom à l'un ou aux deux –

+0

Si cela fonctionne sur un Mac en cours de récupération de place, alors Le point 1 est discutable, mais je ne présume jamais de la récupération de place, car savoir comment gérer manuellement la mémoire est une compétence * extrêmement * précieuse –

10

les autres questions Sauf mentionnées par tout le monde, concentrons-nous sur la raison pour laquelle vous obtenez une erreur hors limites.

Voici le code qui est pertinent.

for (int j = 0; j < QuestionCount - 1; j++) 
{ 

    NSString *SelectedQuestion = [self.QuestionsByLevel objectAtIndex:j]; 
    // ... snip ... 
    if (level != GameLevel) //Always happening in your current code 
     [QuestionsByLevel removeObjectAtIndex:j];  
} 

Voyons ce qui se passe après quelques itérations de ce code.

Première itération:

j == 0 
self.QuestionsByLevel == [Q1, Q2, Q3, Q4, Q5] 

SelectedQuestion = QuestionsByLevel[0] // Q1 

// The following happens because you call removeObjectAtIndex:0 
QuestionsByLevel = [Q2, Q3, Q4, Q5] 

Deuxième Iteration:

j == 1 
self.QuestionsByLevel == [Q2, Q3, Q4, Q5] 
SelectedQuestion = QuestionsByLevel[1] // Q3 

// The following happens because you call removeObjectAtIndex:1 
QuestionsByLevel = [Q2, Q4, Q5] 

Troisième Iteration:

j == 2 
self.QuestionsByLevel == [Q2, Q4, Q5] 
SelectedQuestion = QuestionsByLevel[2] // Q5 

// The following happens because you call removeObjectAtIndex:2 
QuestionsByLevel = [Q2, Q4] 

Quatrième Iteration:

j == 3 
self.QuestionsByLevel == [Q2, Q4] 
SelectedQuestion = QuestionsByLevel[3] // CRASH!!!! 

Pouvez-vous voir le problème? Votre boucle for suppose que vous allez accéder aux objets par leur index, mais après chaque itération, vous supprimez quelque chose du tableau, ce qui déplace tous les index après ce point. Vous ne devriez pas appeler le removeObjectAtIndex:, car vous essayez de parcourir le tableau en même temps.

Si vous essayez simplement d'ignorer un objet spécifique, vous pouvez simplement appeler "continuer" lorsque vous atteignez cet objet. Si vous voulez réellement l'enlever du tableau, appelez simplement [QuestionsByLevel removeObject:GameLevel]. Ou tout ce qui a du sens pour votre situation. Mais faire cela avant vous itérez à travers le tableau.

Questions connexes