web-dev-qa-db-fra.com

Copier en profondeur un tableau NSArray

Existe-t-il une fonction intégrée qui me permet de copier en profondeur un NSMutableArray?

J'ai regardé autour de moi, certaines personnes disent [aMutableArray copyWithZone:nil] fonctionne comme copie profonde. Mais j'ai essayé et cela semble être une copie superficielle.

Actuellement, je copie manuellement avec une boucle 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];
            }
        }
    }
}

mais je voudrais une solution plus propre, plus succincte.

116
ivanTheTerrible

En tant que documentation Apple ,

Si vous n'avez besoin que d'une copie d'un niveau, vous pouvez explicitement en demander une ...

NSMutableArray *newArray = [[NSMutableArray alloc] 
                             initWithArray:oldArray copyItems:YES];

Le code ci-dessus crée un nouveau tableau dont les membres sont des copies superficielles des membres de l'ancien tableau.

Notez que si vous avez besoin de copier en profondeur toute une structure de données imbriquée - ce que le lien Apple docs appelle une "vraie copie en profondeur" - alors cette approche ne suffira pas - voir les autres réponses ici.

206
François P.

Le seul moyen que je connaisse pour le faire facilement consiste à archiver puis à désarchiver immédiatement votre tableau. Cela ressemble à un peu d'un bidouillage, mais c'est en fait explicitement suggéré dans le Documentation Apple sur la copie de collections , qui dit:

Si vous avez besoin d'une véritable copie en profondeur, par exemple lorsque vous avez un tableau de tableaux, vous pouvez archiver puis désarchiver la collection, à condition que le contenu soit conforme au protocole NSCoding. Un exemple de cette technique est donné dans le Listing 3.

Listing 3 Une vraie copie profonde

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
          [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

Le problème est que votre objet doit prendre en charge l'interface NSCoding, car celle-ci sera utilisée pour stocker/charger les données.

Swift 2 Version:

let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
    NSKeyedArchiver.archivedDataWithRootObject(oldArray))
59
Andrew Grant

Copier par défaut donne une copie superficielle

En effet, l'appel de copy est identique à copyWithZone:NULL également appelée copie avec la zone par défaut. L'appel copy ne donne pas une copie complète. Dans la plupart des cas, cela vous donnerait une copie superficielle, mais dans tous les cas cela dépend de la classe. Pour une discussion approfondie, je recommande le Sujets de programmation de collections sur le Apple pour les développeurs).

initWithArray: CopyItems: donne une copie complète à un niveau

NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];

NSCoding est le moyen Apple recommandé de fournir une copie complète

Pour une vraie copie profonde (Array of Arrays), vous aurez besoin de NSCoding et d'archiver/désarchiver l'objet:

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
31
Cameron Lowell Palmer

pour le dictionnaire

NSMutableDictionary *newCopyDict = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)objDict, kCFPropertyListMutableContainers);

pour tablea

NSMutableArray *myMutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(NULL, arrData, kCFPropertyListMutableContainersAndLeaves);

7
Darshit Shah

Non, il n'y a pas quelque chose de construit dans les cadres pour cela. Les collections de cacao supportent les copies superficielles (avec le copy ou le arrayWithArray: _) mais ne parlez même pas du concept de copie en profondeur.

En effet, la "copie profonde" commence à devenir difficile à définir à mesure que le contenu de vos collections commence à inclure vos propres objets personnalisés. Est-ce que "copie en profondeur" signifie chaque objet du graphe d'objets est une référence unique relative à chaque objet dans le graphe d'objet d'origine?

S'il existait un protocole hypothétique NSDeepCopying, vous pourriez le configurer et prendre des décisions dans tous vos objets, mais malheureusement, ce n'est pas le cas. Si vous contrôliez la plupart des objets de votre graphique, vous pouvez créer ce protocole vous-même et le mettre en œuvre, mais vous devez ajouter une catégorie aux classes Foundation si nécessaire.

La réponse de @ AndrewGrant suggérant l'utilisation de l'archivage/désarchivage à clé est un moyen non performant mais correct et propre de réaliser cela pour des objets arbitraires. Ce livre va même jusqu'à suggérer cela ajouter une catégorie à tous les objets qui fait exactement cela pour supporter la copie en profondeur.

4
Ben Zotto

J'ai une solution de contournement si vous essayez d'obtenir une copie complète pour les données compatibles JSON.

Il suffit de prendre NSData de NSArray à l'aide de NSJSONSerialization, puis de recréer un objet JSON. Cela créera une nouvelle et complète copie de NSArray/NSDictionary avec de nouvelles références de mémoire.

Mais assurez-vous que les objets de NSArray/NSDictionary et leurs enfants doivent être sérialisables JSON.

NSData *aDataOfSource = [NSJSONSerialization dataWithJSONObject:oldCopy options:NSJSONWritingPrettyPrinted error:nil];
NSDictionary *aDictNewCopy = [NSJSONSerialization JSONObjectWithData:aDataOfSource options:NSJSONReadingMutableLeaves error:nil];
2
Mrug