Я пытаюсь создать глубокую копию NSMutableDictionary и присвоить его другому NSMutableDictionary. Словарь содержит набор массивов, каждый массив, содержащий имена, и ключ является алфавитом (первая буква тех имен). Таким образом, одна запись в словаре-> 'Adam', 'Apple'. Вот то, что я видел в книге, но я не уверен, работает ли она:
- (NSMutableDictionary *) mutableDeepCopy
{
NSMutableDictionary * ret = [[NSMutableDictionary alloc] initWithCapacity: [self count]];
NSArray *keys = [self allKeys];
for (id key in keys)
{
id oneValue = [self valueForKey:key]; // should return the array
id oneCopy = nil;
if ([oneValue respondsToSelector: @selector(mutableDeepCopy)])
{
oneCopy = [oneValue mutableDeepCopy];
}
if ([oneValue respondsToSelector:@selector(mutableCopy)])
{
oneCopy = [oneValue mutableCopy];
}
if (oneCopy == nil) // not sure if this is needed
{
oneCopy = [oneValue copy];
}
[ret setValue:oneCopy forKey:key];
//[oneCopy release];
}
return ret;
}
Вот то, как я собираюсь назвать этот метод:
self.namesForAlphabets = [self.allNames mutableDeepCopy];
Это будет в порядке? Или это вызовет утечку? (предположите, что я объявляю self.namesForAlphabets как свойство и выпускаю его в dealloc).
IMPORTANT: Вопрос (и мой код ниже) оба касаются очень специфического случая, в котором словарь NSMutableDictionary содержит только массивы строк. Эти решения не будут работать для более сложных примеров. Для более общих случаев смотрите следующее:
Ответ на этот конкретный случай:
Ваш код должен работать, но вам определенно понадобится [однокопический релиз]
. Новый словарь сохранит скопированные объекты при добавлении их с помощью setValue:forKey
, поэтому если вы не вызовете [oneCopy release]
, все эти объекты будут сохранены дважды.
Хорошее эмпирическое правило: если вы присваиваете
, сохраняете
или копируете
что-то, вы также должны освободить
это.
Примечание: вот пример кода, который будет работать только в определенных случаях . Это работает, потому что ваш словарь NSMutableDictionary содержит только массивы строк (больше никакого глубокого копирования не требуется):
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary * ret = [[NSMutableDictionary alloc]
initWithCapacity:[self count]];
NSMutableArray * array;
for (id key in [self allKeys])
{
array = [(NSArray *)[self objectForKey:key] mutableCopy];
[ret setValue:array forKey:key];
[array release];
}
return ret;
}
Предполагая, что все элементы массива реализуют протокол NSCoding, можно делать глубокие копии с помощью архивирования, так как архивирование сохранит мутируемость объектов.
Что-то вроде этого:
id DeepCopyViaArchiving(id<NSCoding> anObject)
{
NSData* archivedData = [NSKeyedArchiver archivedDataWithRootObject:anObject];
return [[NSKeyedUnarchiver unarchiveObjectWithData:archivedData] retain];
}
Но это не особенно эффективно.
Другой метод, который я видел (который совсем не очень эффективен), это использование объекта NSPropertyListSerialization
для сериализации вашего словаря, затем вы де-сериализуете его, но указываете, что вы хотите мутируемые листья и контейнеры.
NSString *errorString = nil;
NSData *binData =
[NSPropertyListSerialization dataFromPropertyList:self.allNames
format:NSPropertyListBinaryFormat_v1_0
errorString:&errorString];
if (errorString) {
// Something bad happened
[errorString release];
}
self.namesForAlphabets =
[NSPropertyListSerialization propertyListFromData:binData
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:NULL
errorDescription:&errorString];
if (errorString) {
// something bad happened
[errorString release];
}
Опять же, это совсем не эффективно.
. Благодаря бесплатному мосту можно также использовать функцию CoreFoundation CFPropertyListCreateDeepCopy
:
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);