¿Cómo hacer una verdadera copia profunda para NSArray y NSDictionary con matrices / diccionario nesteds?

Pregunta: ¿Existe alguna forma de utilizar los métodos Object-C existentes para hacer una copia completa de un NSDictionary o NSArray, que ellos mismos hayan nested diccionarios o matrices dentro de ellos?

Es decir, he leído que el problema puede ser cuando golpea un array o un diccionario nested, solo copia el puntero al elemento nested y no lo copia verdaderamente.

Antecedentes: como un ejemplo para mí, estoy tratando de cargar / guardar la siguiente configuración con NSUserDefaults y cuando cargue necesita convertir las copias inmutables, se obtiene de NSUserDefault a mutable antes de realizar cambios.

  • Artículos ( NSDiccionario )
    • Artículo ( NSDiccionario )
      • aString: NSString
      • aString2: NSString
      • Fecha: NSDate
      • aDate2: NSDate
      • aBool: BOOL
      • aTI1: NSTimeInterval
      • aTI2: NSTimeInterval
      • Palabras clave ( NSArray )
        • palabra clave: NSString
        • palabra clave: NSString

    Hace un par de años, escribí algunos métodos de categorías por exactamente la misma razón, transformando un árbol completo de valores predeterminados de usuario en mutable. Aquí están, ¡utilícelos bajo su propio riesgo! 🙂

    // // SPDeepCopy.h // // Created by Sherm Pendley on 3/15/09. // #import  // Deep -copy and -mutableCopy methods for NSArray and NSDictionary @interface NSArray (SPDeepCopy) - (NSArray*) deepCopy; - (NSMutableArray*) mutableDeepCopy; @end @interface NSDictionary (SPDeepCopy) - (NSDictionary*) deepCopy; - (NSMutableDictionary*) mutableDeepCopy; @end 

     // // SPDeepCopy.m // // Created by Sherm Pendley on 3/15/09. // #import "SPDeepCopy.h" @implementation NSArray (SPDeepCopy) - (NSArray*) deepCopy { unsigned int count = [self count]; id cArray[count]; for (unsigned int i = 0; i < count; ++i) { id obj = [self objectAtIndex:i]; if ([obj respondsToSelector:@selector(deepCopy)]) cArray[i] = [obj deepCopy]; else cArray[i] = [obj copy]; } NSArray *ret = [[NSArray arrayWithObjects:cArray count:count] retain]; // The newly-created array retained these, so now we need to balance the above copies for (unsigned int i = 0; i < count; ++i) [cArray[i] release]; return ret; } - (NSMutableArray*) mutableDeepCopy { unsigned int count = [self count]; id cArray[count]; for (unsigned int i = 0; i < count; ++i) { id obj = [self objectAtIndex:i]; // Try to do a deep mutable copy, if this object supports it if ([obj respondsToSelector:@selector(mutableDeepCopy)]) cArray[i] = [obj mutableDeepCopy]; // Then try a shallow mutable copy, if the object supports that else if ([obj respondsToSelector:@selector(mutableCopyWithZone:)]) cArray[i] = [obj mutableCopy]; // Next try to do a deep copy else if ([obj respondsToSelector:@selector(deepCopy)]) cArray[i] = [obj deepCopy]; // If all else fails, fall back to an ordinary copy else cArray[i] = [obj copy]; } NSMutableArray *ret = [[NSMutableArray arrayWithObjects:cArray count:count] retain]; // The newly-created array retained these, so now we need to balance the above copies for (unsigned int i = 0; i < count; ++i) [cArray[i] release]; return ret; } @end @implementation NSDictionary (SPDeepCopy) - (NSDictionary*) deepCopy { unsigned int count = [self count]; id cObjects[count]; id cKeys[count]; NSEnumerator *e = [self keyEnumerator]; unsigned int i = 0; id thisKey; while ((thisKey = [e nextObject]) != nil) { id obj = [self objectForKey:thisKey]; if ([obj respondsToSelector:@selector(deepCopy)]) cObjects[i] = [obj deepCopy]; else cObjects[i] = [obj copy]; if ([thisKey respondsToSelector:@selector(deepCopy)]) cKeys[i] = [thisKey deepCopy]; else cKeys[i] = [thisKey copy]; ++i; } NSDictionary *ret = [[NSDictionary dictionaryWithObjects:cObjects forKeys:cKeys count:count] retain]; // The newly-created dictionary retained these, so now we need to balance the above copies for (unsigned int i = 0; i < count; ++i) { [cObjects[i] release]; [cKeys[i] release]; } return ret; } - (NSMutableDictionary*) mutableDeepCopy { unsigned int count = [self count]; id cObjects[count]; id cKeys[count]; NSEnumerator *e = [self keyEnumerator]; unsigned int i = 0; id thisKey; while ((thisKey = [e nextObject]) != nil) { id obj = [self objectForKey:thisKey]; // Try to do a deep mutable copy, if this object supports it if ([obj respondsToSelector:@selector(mutableDeepCopy)]) cObjects[i] = [obj mutableDeepCopy]; // Then try a shallow mutable copy, if the object supports that else if ([obj respondsToSelector:@selector(mutableCopyWithZone:)]) cObjects[i] = [obj mutableCopy]; // Next try to do a deep copy else if ([obj respondsToSelector:@selector(deepCopy)]) cObjects[i] = [obj deepCopy]; // If all else fails, fall back to an ordinary copy else cObjects[i] = [obj copy]; // I don't think mutable keys make much sense, so just do an ordinary copy if ([thisKey respondsToSelector:@selector(deepCopy)]) cKeys[i] = [thisKey deepCopy]; else cKeys[i] = [thisKey copy]; ++i; } NSMutableDictionary *ret = [[NSMutableDictionary dictionaryWithObjects:cObjects forKeys:cKeys count:count] retain]; // The newly-created dictionary retained these, so now we need to balance the above copies for (unsigned int i = 0; i < count; ++i) { [cObjects[i] release]; [cKeys[i] release]; } return ret; } @end 

    Creo que algo así debería funcionar.

     NSData *buffer; NSMutableDictionary *_dict1, *_dict2; // Deep copy "all" objects in _dict1 pointers and all to _dict2 buffer = [NSKeyedArchiver archivedDataWithRootObject: _dict1]; _dict2 = [NSKeyedUnarchiver unarchiveObjectWithData: buffer]; 

    La manera más fácil para una copia DEEP es simplemente usar CFPropertyListCreateDeepCopy. Aquí está el ejemplo (con ARC):

      NSDictionary *newDictionary = (__bridge NSDictionary *)(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (__bridge CFPropertyListRef)(originalDictionary), kCFPropertyListImmutable)); 

    mi originalDiccionario en este ejemplo es un NSDictionary

    CFPropertyListRef simplemente puede enviarse a NSDictionary siempre que la entidad de nivel superior de CFPropertyListRef sea CFDictionary.