Глубокое копирование NSArray - PullRequest
116 голосов
/ 15 марта 2009

Есть ли какая-либо встроенная функция, которая позволяет мне глубоко копировать NSMutableArray?

Я оглянулся, некоторые люди говорят, что [aMutableArray copyWithZone:nil] работает как глубокая копия. Но я попытался, и это кажется мелкой копией.

Прямо сейчас я делаю копию с циклом 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];
            }
        }
    }
}

но я бы хотел более чистое, более сжатое решение.

Ответы [ 6 ]

206 голосов
/ 15 марта 2009

Как Apple Docs состояние,

Если вам нужна только одноуровневая копия, вы можете явно вызвать одну ...

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

Приведенный выше код создает новый массив, члены которого являются полными копиями членов старого массива.

Обратите внимание, что если вам необходимо глубоко скопировать всю вложенную структуру данных - то, что связанные документы Apple называют "истинной глубокой копией" - тогда такого подхода будет недостаточно - см. Другие ответы здесь.

59 голосов
/ 15 марта 2009

Единственный способ, которым я знаю, легко сделать это - заархивировать, а затем немедленно разархивировать ваш массив. Это похоже на хак, но на самом деле это явно указано в Документации Apple по копированию коллекций , в которой говорится:

Если вам нужна настоящая глубокая копия, например, когда у вас есть массив массивов, вы можете заархивировать, а затем разархивировать коллекцию при условии, что все содержимое соответствует протоколу NSCoding. Пример этого метода показан в листинге 3.

Листинг 3 Истинная глубокая копия

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

Смысл в том, что ваш объект должен поддерживать интерфейс NSCoding, так как он будет использоваться для хранения / загрузки данных.

Swift 2 Версия:

let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
    NSKeyedArchiver.archivedDataWithRootObject(oldArray))
31 голосов
/ 23 февраля 2013

Копирование по умолчанию дает мелкую копию

Это потому, что вызов copy аналогичен copyWithZone:NULL, также известному как копирование с зоной по умолчанию. Вызов copy не приводит к глубокому копированию. В большинстве случаев это даст вам поверхностную копию, но в любом случае это зависит от класса. Для подробного обсуждения я рекомендую Темы программирования коллекций на сайте Apple Developer.

initWithArray: CopyItems: дает одноуровневую глубокую копию

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

NSCoding это рекомендуемый Apple способ предоставления глубокой копии

Для истинного глубокого копирования (Array of Arrays) вам понадобится NSCoding и архивирование / разархивирование объекта:

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
7 голосов
/ 14 октября 2015

для диктонары

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

для массива

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

4 голосов
/ 02 ноября 2013

Нет, что-то не встроено в рамки для этого. Коллекции какао поддерживают мелкие копии (с помощью методов copy или arrayWithArray:), но даже не говорят о концепции глубокого копирования.

Это связано с тем, что становится трудно определить «глубокое копирование» по мере того, как начинается содержимое ваших коллекций, включая ваши собственные пользовательские объекты. Означает ли "глубокая копия" каждый объект в графе объектов уникальную ссылку относительно каждого объекта в исходном графе объектов?

Если бы был какой-то гипотетический протокол NSDeepCopying, вы могли бы настроить его и принимать решения по всем вашим объектам, но, к сожалению, его нет. Если вы управляете большинством объектов в своем графике, вы можете создать этот протокол самостоятельно и реализовать его, но при необходимости вам потребуется добавить категорию в классы Foundation.

@ Ответ AndrewGrant, в котором предлагается использовать архивирование / разархивирование с использованием ключа, - это нецелесообразный, но правильный и чистый способ достижения этого для произвольных объектов. Эта книга даже заходит так далеко, что предлагает добавить категорию ко всем объектам, которая делает именно это для поддержки глубокого копирования.

2 голосов
/ 24 июля 2015

У меня есть обходной путь, если я пытаюсь получить глубокое копирование для данных, совместимых с JSON.

Просто возьмите NSData из NSArray с помощью NSJSONSerialization и затем заново создайте JSON Object, это создаст полную новую и свежую копию NSArray/NSDictionary с новыми ссылками на них из памяти.

Но убедитесь, что объекты NSArray / NSDictionary и их дочерние элементы должны быть сериализуемыми в JSON.

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