Copiando em profundidade um NSArray

Existe alguma function interna que me permite copiar profundamente um NSMutableArray ?

Eu olhei em volta, algumas pessoas dizem [aMutableArray copyWithZone:nil] funciona como cópia. Mas eu tentei e parece ser uma cópia superficial.

Agora eu estou fazendo manualmente a cópia com um loop 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]; } } } } 

mas eu gostaria de uma solução mais limpa e mais sucinta.

Como a Apple docs afirma,

Se você precisa apenas de uma cópia com um nível de profundidade, você pode explicitamente solicitar uma …

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

O código acima cria uma nova matriz cujos membros são cópias rasas dos membros da matriz antiga.

Observe que, se você precisar copiar profundamente toda uma estrutura de dados aninhada – o que os documentos da Apple vinculados chamam de “cópia profunda verdadeira” – essa abordagem não será suficiente – veja as outras respostas aqui.

A única maneira de fazer isso é arquivar e desarquivar imediatamente o array. Parece um pouco um hack, mas na verdade é explicitamente sugerido na documentação da Apple sobre a cópia de collections , que afirma:

Se você precisar de uma cópia profunda verdadeira, por exemplo, quando tiver uma matriz de matrizes, poderá arquivar e desarquivar a coleção, desde que todos os conteúdos estejam em conformidade com o protocolo NSCoding. Um exemplo dessa técnica é mostrado na Listagem 3.

Listagem 3 Uma cópia profunda verdadeira

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

O problema é que seu object deve suportar a interface NSCoding, já que isso será usado para armazenar / carregar os dados.

Versão Swift 2:

 let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData( NSKeyedArchiver.archivedDataWithRootObject(oldArray)) 

Copiar por padrão fornece uma cópia superficial

Isso ocorre porque a copy chamada é a mesma que copyWithZone:NULL também conhecido como cópia com a zona padrão. A chamada de copy não resulta em uma cópia profunda. Na maioria dos casos, você obtém uma cópia superficial, mas, de qualquer forma, depende da class. Para uma discussão completa, recomendo os Tópicos de Programação de Coleções no site Apple Developer.

initWithArray: CopyItems: fornece uma cópia profunda de um nível

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

NSCoding é a maneira recomendada pela Apple para fornecer uma cópia detalhada

Para uma cópia profunda verdadeira (Array of Arrays) você precisará do NSCoding e arquivar / desarquivar o object:

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

Para Dictonary

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

Para matriz

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

Não, não há algo embutido nos frameworks para isso. As collections de cacau suportam cópias superficiais (com os methods copy ou arrayWithArray: , mas nem mesmo falam sobre um conceito de cópia profunda.

Isso ocorre porque a “cópia profunda” começa a se tornar difícil de definir à medida que o conteúdo de suas collections começa, incluindo seus próprios objects personalizados. A “cópia profunda” significa que cada object no gráfico de object é uma referência única relativa a todos os objects no gráfico de object original?

Se houvesse algum protocolo hipotético NSDeepCopying , você poderia configurar isso e tomar decisões em todos os seus objects, mas infelizmente não há. Se você controlasse a maioria dos objects em seu gráfico, poderia criar esse protocolo e implementá-lo, mas seria necessário adicionar uma categoria às classs Foundation, conforme necessário.

A resposta de @ AndrewGrant sugerindo o uso de arquivamento / desarquivamento com chave é um modo não-efetivo, mas correto e limpo de conseguir isso para objects arbitrários. Este livro ainda vai tão longe, então sugiro adicionar uma categoria a todos os objects que fazem exatamente isso para apoiar a cópia profunda.

Eu tenho uma solução alternativa para tentar obter uma cópia detalhada para dados compatíveis com JSON.

Simplesmente pegue NSData de NSArray usando NSJSONSerialization e então recrie JSON Object, isso criará uma cópia nova e completa de NSArray/NSDictionary com novas referências de memory deles.

Mas certifique-se de que os objects NSArray / NSDictionary e seus filhos sejam JSON serializáveis.

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