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.
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.
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.
let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
NSKeyedArchiver.archivedDataWithRootObject(oldArray))
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).
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];
NSCoding
est le moyen Apple recommandé de fournir une copie complètePour 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]];
pour le dictionnaire
NSMutableDictionary *newCopyDict = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)objDict, kCFPropertyListMutableContainers);
pour tablea
NSMutableArray *myMutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(NULL, arrData, kCFPropertyListMutableContainersAndLeaves);
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.
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];