Detalles sobre el uso de enumeración rápida en una copy de un NSMutableArray para eliminar objects

Aprendí de la manera más difícil que no puedes eliminar objects de un NSMutableArray cuando estás recorriendo los objects en él.

[< array_object > copy] bucle a través de [< array_object > copy] lugar de < array_object > soluciona.

Sin embargo, tengo algunas preguntas sin respuesta sobre las cuales quisiera participar de los gurús de Objective-C.

  1. En este primer bucle for, esperaba que cada uno de los nextObject apunte a diferentes memorys (es decir, pensé que el array msgDetail tendrá una list de pointers, cada uno apuntando a la dirección del NSDictionary que contiene un índice de matriz particular). Pero todas las impresiones %p nextObject están dando el mismo valor. ¿Porqué es eso?

     for(NSDictionary *nextObject in msgDetailArray) { NSLog(@"Address = %p, value = %@",&nextObject,nextObject) ; //Doing [msgDetailArray removeObject:nextObject] here based on some condition fails } 
  2. En el segundo bucle for, la dirección de nextObject NSDictionary s es diferente de la impresa en el primer bucle for.

     for(id nextObject in [msgDetailArray copy]) { NSLog(@"In copy Address = %p, value = %@",&nextObject,nextObject) ; //Doing [msgDetailArray removeObject:nextObject] here based on some condition succeeds and it also removes it from the original array even though I am looping through a copy } 
  3. Sin embargo, cuando [msgDetailArray copy] , y luego hago un removeObject: lo elimina del original msgDetailArray . ¿Cómo elimina removeObject: hacer esto? ¿Realmente usa los contenidos del dictionary y elimina un object que coincida con el contenido? Pensé que todo lo que hace es comprobar si hay un object que está en la misma location de memory y eliminarlo (Basado en 2, supongo que la dirección de memory que contiene los dictionarys en [msgDetailArray copy] no son las mismas que las direcciones en el original msgDetailArray ). Si realmente está usando contenidos, tendré que tener mucho cuidado en caso de que haya inputs duplicadas.

     for(id nextObject in msgDetailArray) { NSLog(@"Address = %p, value = %@",&nextObject,nextObject) ; //test what is left in msgDetailArray. I see that doing removeObject on [msgDetailArray copy] does remove it from the original too. How is removeObject working (is it actually contents of dictionary) } 

Copiar una matriz hace una "copy superficial". Crea un nuevo object de array, luego almacena pointers a los objects en el primer array. No crea nuevos objects.

Piensa en una matriz como una libreta de direcciones. Enumera las direcciones de las casas de tus amigos. Si hago una copy de su libreta de direcciones, entonces tengo una copy de la list de las direcciones. Las libretas de direcciones no contienen casas.

Si borro una input de mi copy de la libreta de direcciones, no borra la misma input de su libreta de direcciones. Tampoco destruye ninguna casa.

En su código de bucle 2, está recorriendo la copy, y luego le dice a su matriz original que elimine ciertos objects. Se eliminan de la matriz original, pero no de la copy. (Lo cual es bueno, porque como dices, mutar una matriz a medida que se itera a través de ella provoca un locking).

Tenga en count que las matrices pueden contener más de un puntero al mismo object, al igual que las direcciones pueden contener la misma dirección más de una vez. (Si un amigo cambia su nombre cuando se casa, puede escribirla en su dirección debajo del nombre de la persona casada y dejar la input debajo de su apellido de soltera. Ambas direcciones apuntan a la misma casa).

Tenga en count que el método removeObject que está utilizando elimina TODAS las inputs de un object. Es como si estuvieras diciendo "borra cada input en mi libreta de direcciones para la calle 123 Elm". Si solo quieres eliminar una input de un set duplicado, entonces debes usar removeObjectAtIndex en su lugar, y deberías cambiar tu código para que funcione.

  1. La operación de copy copy el contenedor, no su contenido. Tienes una nueva list de pointers para los mismos objects.

  2. &nextObject es la dirección de la variable, no el object. La dirección del object es el valor de la variable: NSLog(@"%p", nextObject);

  3. removeObject: compara su argumento con el contenido de la colección usando isEqual: que generalmente no se compara por dirección, sino por algún tipo de valoración semántica definida por la class.

Incluso dejando a un lado la prohibición de mutar dentro de un ciclo de enumeración rápida, no podrías eliminar objects de la copy, ya que copy hace una copy inmutable . Necesita mutableCopy para poder cambiar la nueva instancia.

Pero todas las impresiones %p s dan el mismo valor. ¿Porqué es eso?

Porque &nextObject es la dirección de la variable nextObject , una variable de bucle utilizada en su ejemplo de código; su tipo es un puntero a un puntero. nextObject es también un puntero, así que si desea imprimir la dirección del object, todo lo que necesita hacer es convertirlo en void* :

 NSLog(@"Address = %p, value = %@", (void*)nextObject, nextObject); 

En el segundo bucle for, la dirección de nextObject NSDictionaries es diferente de la que se imprimió en el primer bucle for

Porque esa es la dirección de una variable diferente .

cuando [msgDetailArray copy] , y luego hago un removeObject , lo elimina del original msgDetailArray .

La copy a través de la cual está dando vueltas está en un object temporal invisible. Es una copy inmutable respaldada por la matriz original.

La llamada [msgDetailArray removeObject:nextObject] opera en la matriz original. Si no desea este efecto, almacene la copy en una variable y elimine los elementos de ella:

 NSMutableArray *mutableCopy = [msgDetailArray mutableCopy]; ... [mutableCopy removeObject:nextObject]; 

Esto no tocará la matriz original. Sin embargo, no puede iterar a mutableCopy al mismo time que borra elementos de él.