Глубокое копирование 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'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 ответов
Как утверждают Apple,
Если вам нужна только одноуровневая копия, вы можете явно вызвать одну...
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:oldArray copyItems:YES];
Приведенный выше код создает новый массив, члены которого являются мелкими копиями членов старого массива.
Обратите внимание, что если вам нужно глубоко скопировать всю вложенную структуру данных - то, что связанные документы Apple называют "истинной глубокой копией" - тогда такого подхода будет недостаточно - см. Другие ответы здесь.
Единственный способ, который я знаю, это легко сделать, это заархивировать, а затем немедленно разархивировать ваш массив. Это похоже на хак, но на самом деле это явно указано в документации Apple по копированию коллекций, в которой говорится:
Если вам нужна настоящая глубокая копия, например, когда у вас есть массив массивов, вы можете заархивировать, а затем разархивировать коллекцию при условии, что все содержимое соответствует протоколу NSCoding. Пример этого метода показан в листинге 3.
Перечисление 3 Истинная глубокая копия
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:oldArray]];
Уловка в том, что ваш объект должен поддерживать интерфейс NSCoding, так как он будет использоваться для хранения / загрузки данных.
Версия Swift 2:
let trueDeepCopyArray = NSKeyedUnarchiver.unarchiveObjectWithData(
NSKeyedArchiver.archivedDataWithRootObject(oldArray))
Копия по умолчанию дает мелкую копию
Это потому что зовет copy
такой же как copyWithZone:NULL
также известный как копирование с зоной по умолчанию. copy
вызов не приводит к глубокой копии. В большинстве случаев это даст вам поверхностную копию, но в любом случае это зависит от класса. Для подробного обсуждения я рекомендую темы программирования коллекций на сайте Apple для разработчиков.
initWithArray: CopyItems: дает одноуровневую копию
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:someArray copyItems:YES];
NSCoding
Apple рекомендует способ предоставить глубокую копию
Для настоящей глубокой копии (Array of Arrays) вам понадобится NSCoding
и архивировать / разархивировать объект:
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
Для диктонары
NSMutableDictionary *newCopyDict = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)objDict, kCFPropertyListMutableContainers);
Для массива
NSMutableArray *myMutableArray = (NSMutableArray *)CFPropertyListCreateDeepCopy(NULL, arrData, kCFPropertyListMutableContainersAndLeaves);
Нет, для этого нет чего-то встроенного в рамки. Коллекции какао поддерживают мелкие копии (с copy
или arrayWithArray:
методы), но даже не говорите о концепции глубокого копирования.
Это связано с тем, что "глубокое копирование" становится все труднее определить по мере того, как начинается содержимое ваших коллекций, включая ваши собственные пользовательские объекты. Означает ли "глубокая копия", что каждый объект в графе объектов является уникальной ссылкой относительно каждого объекта в исходном графе объектов?
Если бы была какая-то гипотетическая NSDeepCopying
протокол, вы можете настроить это и принимать решения во всех ваших объектах, но, к сожалению, нет. Если вы управляете большинством объектов на вашем графике, вы можете создать этот протокол самостоятельно и реализовать его, но при необходимости вам нужно будет добавить категорию в классы Foundation.
@ Ответ AndrewGrant о том, что использование архивирования / разархивирования с использованием ключа является неэффективным, но правильным и чистым способом достижения этого для произвольных объектов. Эта книга даже идет так далеко, поэтому предлагаем добавить категорию ко всем объектам, которая делает именно это для поддержки глубокого копирования.
У меня есть обходной путь, если я пытаюсь получить глубокое копирование для 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];