Подтвердить что ты не робот

Глубокое копирование NSArray

Есть ли встроенная функция, которая позволяет мне скопировать 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 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];
            }
        }
    }
}

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

4b9b3361

Ответ 1

В документации Apple о глубоких копиях прямо говорится:

Если вам нужна только одноуровневая копия:

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

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

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

Ответ 2

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

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

Перечисление 3 Истинная глубокая копия

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

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

Версия Swift 2:

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

Ответ 3

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

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

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

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

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

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

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

Ответ 4

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

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

Для массива

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

Ответ 5

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

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

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

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

Ответ 6

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

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

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

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