objective c - Copia profunda de un NSArray
objective-c cocoa-touch (6)
¿Hay alguna función incorporada que me permita copiar profundamente un NSMutableArray
?
Miré a mi alrededor, algunas personas dicen que [aMutableArray copyWithZone:nil]
funciona como una copia profunda. Pero lo intenté y parece ser una copia superficial.
En este momento estoy haciendo manualmente la copia con un ciclo for
:
//deep copy a 9*9 mutable array to a passed-in reference array
-deepMuCopy : (NSMutableArray*) array
toNewArray : (NSMutableArray*) arrayNew {
[arrayNew removeAllObjects];//ensure it''s clean
for (int y = 0; y<9; y++) {
[arrayNew addObject:[NSMutableArray new]];
for (int x = 0; x<9; x++) {
[[arrayNew objectAtIndex:y] addObject:[NSMutableArray new]];
NSMutableArray *aDomain = [[array objectAtIndex:y] objectAtIndex:x];
for (int i = 0; i<[aDomain count]; i++) {
//copy object by object
NSNumber* n = [NSNumber numberWithInt:[[aDomain objectAtIndex:i] intValue]];
[[[arrayNew objectAtIndex:y] objectAtIndex:x] addObject:n];
}
}
}
}
pero me gustaría una solución más limpia y más sucinta.
Copiar de forma predeterminada da una copia superficial
Esto se debe a que la copy
llamada es igual a copyWithZone:NULL
también conocida como copia con la zona predeterminada. La llamada de copy
no da como resultado una copia profunda. En la mayoría de los casos, le daría una copia superficial, pero en cualquier caso depende de la clase. Para una discusión exhaustiva, recomiendo los temas de programación de colecciones en el sitio del desarrollador de Apple.
initWithArray: CopyItems: proporciona una copia profunda de un nivel
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];
NSCoding
es la forma recomendada por Apple de proporcionar una copia profunda
Para una verdadera copia profunda (Array of Arrays) necesitará NSCoding
y archivar / desarchivar el objeto:
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
Como dice el Apple ,
Si solo necesita una copia de un nivel de profundidad, puede llamar explícitamente a uno ...
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:oldArray copyItems:YES];
El código anterior crea una nueva matriz cuyos miembros son copias superficiales de los miembros de la matriz anterior.
Tenga en cuenta que si necesita copiar profundamente una estructura de datos anidada completa, lo que los documentos Apple vinculados llaman una "verdadera copia profunda" , entonces este enfoque no será suficiente: consulte las otras respuestas aquí.
La única forma que conozco de hacer esto fácilmente es archivar y luego inmediatamente desarchivar tu matriz. Se siente como un truco, pero en realidad se sugiere explícitamente en la Documentación de Apple sobre copias de colecciones , que dice:
Si necesita una verdadera copia profunda, como cuando tiene una matriz de matrices, puede archivar y luego desarchivar la colección, siempre que el contenido se ajuste al protocolo NSCoding. Un ejemplo de esta técnica se muestra en el Listado 3.
Listado 3 Una verdadera copia profunda
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:oldArray]];
El problema es que su objeto debe ser compatible con la interfaz de NSCoding, ya que se usará para almacenar / cargar los datos.
Swift 2 Version:
let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
NSKeyedArchiver.archivedDataWithRootObject(oldArray))
No, no hay algo integrado en los marcos para esto. Las colecciones de cacao admiten copias superficiales (con la copy
o la arrayWithArray:
métodos), pero ni siquiera hablan de un concepto de copia profunda.
Esto se debe a que la "copia profunda" comienza a ser difícil de definir a medida que los contenidos de sus colecciones comienzan incluyendo sus propios objetos personalizados. ¿La "copia profunda" significa que cada objeto en el gráfico del objeto es una referencia única relativa a cada objeto en el gráfico del objeto original?
Si existiera algún protocolo hipotético de NSDeepCopying
, podría configurarlo y tomar decisiones en todos sus objetos, pero desafortunadamente no existe. Si controlaba la mayoría de los objetos en su gráfico, podría crear este protocolo usted mismo e implementarlo, pero necesitaría agregar una categoría a las clases Foundation según sea necesario.
La respuesta de @ AndrewGrant, que sugiere el uso de archivar / desarchivar con clave, es una forma no correcta pero correcta y limpia de lograr esto para objetos arbitrarios. Este libro incluso va tan lejos, así que sugiere agregar una categoría a todos los objetos que haga exactamente eso para respaldar la copia profunda.
Tengo una solución si trato de lograr una copia profunda para datos compatibles con JSON.
Simplemente tome NSData
de NSArray
usando NSJSONSerialization
y luego recree el objeto JSON, esto creará una copia nueva y nueva de NSArray/NSDictionary
con nuevas referencias de memoria de ellos.
Pero asegúrese de que los objetos de NSArray / NSDictionary y sus hijos deben ser serializables por JSON.
NSData *aDataOfSource = [NSJSONSerialization dataWithJSONObject:oldCopy options:NSJSONWritingPrettyPrinted error:nil];
NSDictionary *aDictNewCopy = [NSJSONSerialization JSONObjectWithData:aDataOfSource options:NSJSONReadingMutableLeaves error:nil];
Para Dictonary
NSMutableDictionary *newCopyDict = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)objDict, kCFPropertyListMutableContainers);
Para Matriz
NSMutableArray *myMutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(NULL, arrData, kCFPropertyListMutableContainersAndLeaves);