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

Копирование NSArray с изменяемыми копиями исходных элементов

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

Поэтому я использую следующее в методе getter моего класса, который содержит массив "master":

[[NSMutableArray alloc] initWithArray:masterArray copyItems:YES];

Однако, похоже, это делает все словари внутри неизменяемыми. Как я могу избежать этого?

Я думаю, что здесь что-то не хватает. Любая помощь будет высоко оценена!

4b9b3361

Ответ 1

Другой подход, который вы могли бы предпринять, - использовать функцию CFPropertyListCreateDeepCopy() (в структуре CoreFoundation), передав в kCFPropertyListMutableContainers аргумент mutabilityOption. Код будет выглядеть так:

NSMutableArray* originalArray;
NSMutableArray* newArray;

newArray = (NSMutableArray*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFPropertyListRef)originalArray, kCFPropertyListMutableContainers);

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

Ответ 2

Копирование массива создаст копию объекта массива со ссылками на исходные объекты содержимого (соответственно сохраненные.)

В документации -initWithArray:copyItems: инициализирует новый массив с копиями элементов в исходном массиве. Эта копия создается путем отправки исходных объектов контента -copyWithZone:, которые создадут неизменяемую копию в случае изменяемых объектов.

Если вам нужно другое поведение (т.е. изменяемые копии объектов контента или глубокие копии объектов контента), вам нужно написать свою собственную функцию удобства/метода для этого.

Ответ 3

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

Как правило, когда я вижу слова "массив словарей", я понимаю, что вы используете словари в качестве замены для объектов модели. Не делай этого. Напишите свои собственные классы моделей; все становится намного проще, когда у вас есть свои собственные настраиваемые свойства и методы поведения для работы. (Некоторые вещи больше, чем другие: реализация поддержки AppleScript практически невозможна без соответствующего слоя модели.)

И как только у вас появятся реальные объекты модели, вы можете реализовать NSCopying в них, и вам не нужно беспокоиться о mutable versus immutable, так как вы, вероятно, не будете иметь различия в различиях в ваших реальных классах моделей. (Я не знаю о других, но я никогда не делал такого различия в своих классах моделей.) Тогда вы можете просто использовать существующий метод NSArray initWithArray:copyItems:.

Ответ 4

Джим прав относительно -initWithArray:copyItems, отправив -copyWithZone: для каждого элемента. Чтобы получить измененные копии элементов массива, вам нужно отправить -mutableCopyWithZone: (или просто -mutableCopy для краткости) каждому элементу. Это довольно просто:

NSMutableArray *masterArray = ...
NSMutableArray *clone = [NSMutableArray arrayWithCapacity:[masterArray count]];
for (id anObject in masterArray)
    [clone addObject:[anObject mutableCopy]]; // OR [clone addObject:anObject];

Однако в вашем объяснении проблемы есть более глубокий вопрос: кажется, вы хотите, чтобы и массив, и его элементы (словари) были изменчивыми, но есть несколько тонких моментов, которые должны быть устранены, особенно в что ваша копия, переданная другим объектам, должна быть изменена без изменения оригинала ". В зависимости от того, что вы имеете в виду, это может быть довольно сложно гарантировать и реализовать.

Например, предположим, что исходный массив содержит несколько изменяемых словарей. Создание измененных копий этих первых двух уровней означает, что кто-то, кто получает копию, может изменить свой собственный массив и словари в массиве без непосредственного изменения исходного массива или словаря. Однако, если словарь содержит изменяемые объекты (например, NSMutableArray, NSMutableString и т.д.), Код с использованием "копии" может косвенно изменять содержимое словаря, используя только ссылку на изменяемую копию.

Переменные копии являются "неглубокими", то есть копируется только первый уровень, а копия имеет указатели на те же элементы, что и исходная структура. Таким образом, гарантируя отсутствие связи между оригиналом и копией (по крайней мере, вручную), требуется проскальзывать по всей структуре и делать копии. Это может усложниться, если некоторые элементы не соответствуют NSCopying или NSMutableCopying.

Самое простое и быстрое решение - использовать простой код выше или просто вернуть ссылку на главный массив. Это требует доверия к тому, что код клиента не будет изменять массив, поэтому это может не работать во всех ситуациях, но если вы также управляете вызывающим кодом, это может быть предпочтительным. С другой стороны, если вы определенно хотите полностью отдельную копию, подумайте об использовании архивации NSCoding/keyed:

NSMutableArray *masterArray = ...
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:masterArray];
NSMutableArray *clone = [NSKeyedUnarchiver unarchiveObjectWithData:data];

В принципе, это преобразует все в необработанные байты данных, а затем воссоздает его в новый набор объектов. Для этого все объекты в словаре должны соответствовать протоколу NSCoding. Это может занять немного времени, но это элегантный универсальный способ гарантировать уникальность объекта. Это, безусловно, имеет отличную от нуля стоимость, но если вы абсолютно должны гарантировать, что побочных эффектов не будет, оно должно работать.