Evitar que “NSArray se mutó mientras se enumeraba”

Tengo un NSMutableArray que almacena mousejoints para una simulación de física Box2d. Cuando use más de un dedo para jugar obtendré excepciones que indiquen

NSArray se mutó mientras se enumeraba

Sé que esto se debe a que estoy eliminando objetos de la matriz mientras también enumero a través de ella, invalidando la enumeración.

Lo que quiero saber es, ¿cuál es la mejor estrategia para resolver esto en el futuro? He visto algunas soluciones en línea: @synchronized , copiando la matriz antes de enumerar o poner la unión táctil en una matriz de basura para su eliminación posterior (que no estoy seguro de que funcione, porque necesito eliminar la junta del mouse de la matriz directamente) después de sacarlo del mundo).

Siempre puede iterar sin un enumerador. Lo que significa un bucle for regular, y cuando elimina un objeto: – disminuir la variable de índice y continuar; . si está almacenando en caché el recuento de la matriz antes de ingresar al bucle for, entonces asegúrese de disminuirlo también al eliminar un objeto.

De todos modos, no veo por qué una matriz con objetos para su eliminación posterior sería un problema. No sé la situación exacta que está teniendo y las tecnologías involucradas, pero teóricamente no debería haber un problema. Porque en la mayoría de los casos, al usar este método, simplemente no puede hacer nada en la primera enumeración y hacer el trabajo real al enumerar la matriz de eliminación. Y si tiene una situación en la que en la primera enumeración está comprobando algo nuevamente contra la misma matriz y necesita saber que los objetos ya no están allí, puede simplemente agregar una marca para ver si están en la matriz de eliminación.

De todos modos, espero haber ayudado. ¡Buena suerte!

Puedes hacer algo como esto:

 NSArray *tempArray = [yourArray copy]; for(id obj in tempArray) { //It's safe to remove objects from yourArray here. } [tempArray release]; 

La forma más simple es enumerar al revés a través de la matriz, lo que significa que el siguiente índice no se verá afectado cuando elimine un objeto.

 for (NSObject *object in [mutableArray reverseObjectEnumerator]) { // it is safe to test and remove the current object if (AddTestHere) { [savedRecentFiles removeObject: object]; } } 

La operación de locking (@synchronized) es mucho más rápida que copiar toda la matriz una y otra vez. Por supuesto, esto depende de cuántos elementos tiene la matriz y con qué frecuencia se ejecuta. Imagine que tiene 10 hilos ejecutando este método simultáneamente:

 - (void)Callback { [m_mutableArray addObject:[NSNumber numberWithInt:3]]; //m_mutableArray is instance of NSMutableArray declared somewhere else NSArray* tmpArray = [m_mutableArray copy]; NSInteger sum = 0; for (NSNumber* num in tmpArray) sum += [num intValue]; //Do whatever with sum } 

Está copiando n + 1 objetos cada vez. Puede usar el locking aquí, pero ¿qué pasa si hay 100k elementos para iterar? La matriz se bloqueará hasta que se complete la iteración, y otros hilos tendrán que esperar hasta que se libere el locking. Creo que copiar objetos aquí es más efectivo, pero también depende de cuán grande sea ese objeto y qué haces en la iteración. El locking debe mantenerse siempre durante el período de tiempo más corto. Entonces usaría el locking para algo como esto.

 - (void)Callback { NSInteger sum = 0; @synchronized(self) { if(m_mutableArray.count == 5) [m_mutableArray removeObjectAtIndex:4]; [m_mutableArray insertObject:[NSNumber numberWithInt:3] atIndex:0]; for (NSNumber* num in tmpArray) sum += [num intValue]; } //Do whatever with sum } 

Todo lo anterior no funcionó para mí, pero esto hizo:

  while ([myArray count] > 0) { [:[myArray objectAtIndex:0]]; } 

Nota: esto lo eliminará todo. Si necesita elegir qué elementos se deben eliminar, esto no funcionará.

Usar un bucle for en lugar de enumerar está bien. Pero una vez que comience a eliminar los elementos de la matriz, tenga cuidado si está utilizando subprocesos. No es suficiente disminuir el contador ya que puede estar borrando las cosas equivocadas. Una de las formas correctas es crear una copia de la matriz, repetir la copia y eliminarla del original.

edc1591 es el método correcto.

[Brendt: necesita eliminar del original mientras itera a través de la copia]

 for(MyObject *obj in objQueue) { //[objQueue removeObject:someof];//NEVER removeObject in a for-in loop [self dosomeopearitions]; } //instead of remove outside of the loop [objQueue removeAllObjects]; 

Tuve el mismo problema, y ​​la solución es enumerar la copia y mutar el original, como edc1591 correctamente mencionado. Probé el código y ya no recibo el error. Si aún obtiene el error, significa que todavía está mutando la copia (en lugar del original) dentro del ciclo for.

 NSArray *copyArray = [[NSArray alloc] initWithArray:originalArray]; for (id obj in copyArray) { // tweak obj as you will [originalArray setObject:obj forKey:@"kWhateverKey"]; } 

Tal vez eliminó o agregó objetos a su mutableAray mientras lo enumeraba. En mi situación, el error apareció en este caso.

Me he encontrado con este error y en mi caso fue por la situación de múltiples subprocesos. Un hilo estaba enumerando una matriz, mientras que otro hilo eliminaba objetos de la misma matriz al mismo tiempo. Espero que esto ayude a alguien.