web-dev-qa-db-fra.com

Objective-C NSMutableArray a été muté lors de l'énumération?

Je suis un peu tombé sur l'erreur dans laquelle vous essayez de supprimer des objets d'un NSMutableArray alors que d'autres objets y sont ajoutés ailleurs. Pour rester simple, je ne sais pas comment y remédier. Voici ce que je fais:

J'ai 4 timers appelant 4 méthodes différentes qui ajoutent un objet au même tableau. Maintenant, quand j’appuie sur un certain bouton, j’ai besoin de supprimer tous les objets du tableau (ou du moins certains). Donc, j'ai d'abord essayé d'invalider les 4 minuteurs, puis de faire le travail que je veux avec le tableau, puis de lancer les minuteries. Je pensais que cela aurait fonctionné puisque je n'utilise plus les minuteries pour énumérer l'ensemble du tableau, mais apparemment, cela ne fonctionne pas.

Des suggestions ici?

37
Seerex

Cela n'a rien à voir avec vos minuteries. Parce que (je suppose) vos timers travaillent tous sur le même thread que votre méthode de modification, vous n'avez pas besoin de les arrêter et de les démarrer. iOS n'utilise pas de modèle d'interruption pour les rappels par minuterie, ils doivent attendre leur tour comme tout autre événement :)

Vous faites probablement quelque chose comme

for (id object in myArray)
   if (someCondition)
       [myArray removeObject:object];

Vous ne pouvez pas éditer un tableau mutable pendant que vous le parcourez, vous devez donc créer un tableau temporaire pour contenir les éléments que vous souhaitez supprimer.

// Find the things to remove
NSMutableArray *toDelete = [NSMutableArray array];
for (id object in myArray)
   if (someCondition)
       [toDelete addObject:object];

// Remove them
[myArray removeObjectsInArray:toDelete];
108
deanWombourne

Vous pouvez le faire de cette façon:

for (id object in [myArray copy])
   if (someCondition)
       [myArray removeObject:object];

Comme @deanWombourne a déclaré: "vous ne pouvez pas éditer un tableau mutable tant que vous le parcourez", je vais donc créer ici une copie auto-supprimée de votre tableau d'origine pour énumérer les objets, de manière à pouvoir supprimer tout ce que tu veux.

Plus clair et moins code de la chaudière (je pense!).

Update : Suppression de l'appel à libération automatique car il s'agissait d'une réponse ancienne, pré-ARC.

24
ferostar

Vous pouvez utiliser la directive @synchronized () pour verrouiller le tableau pendant sa mutation:

if (someCondition) {
    @synchronized(yourArray) {
        [yourArray removeObject:someObject];
    }
}

Plus d'informations sur http://developer.Apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocThreading.html

5
Niklas Berglund

Tu peux essayer:

for (id object in [myArray reverseObjectEnumerator])
if (someCondition)
   [myArray removeObject:object];

Si vous supprimez un objet à l’index x => L’index des objets à l’index x + 1, x + 2 .... sera modifié . Ainsi, lorsque vous utiliserez reverseObjectEnumerator , l’index des objets du tableau après supprimer certains objets seront toujours corriger. 

J'espère que cela vous aidera ... (la réponse acceptée est une solution claire).

0
Nhat Dinh

Bien que par dessus tout soient vrais ... je partagerais mon expérience avec 

muté pendant l'énumération

Ce que je faisais était simple mais totalement erroné: 

for (id obj  in d.dataDashBoardGraph) {
    [tmpData addObject:[obj valueForKey:[[dataToShow[i] componentsSeparatedByString:@"_"] objectAtIndex:1]]];

    ...
}

Même cela a causé mutated being enumerated error. S'en débarasser: 

for (id obj  in d.dataDashBoardGraph) {
   NSString *data = [dataToShow[i] copy];
   [tmpData addObject:[obj valueForKey:[[data componentsSeparatedByString:@"_"] objectAtIndex:1]]];

    ... 
}

Ensuite, cela a fonctionné parfaitement.

0
user3693546