глубоко изменяемая копия NSMutableDictionary

Я пытаюсь создать глубокую копию 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;
}
  • должен [onecopy выпуск] быть там или нет?
  • Вот то, как я собираюсь назвать этот метод:

    self.namesForAlphabets = [self.allNames mutableDeepCopy];

Это будет в порядке? Или это вызовет утечку? (предположите, что я объявляю self.namesForAlphabets как свойство и выпускаю его в dealloc).

36
задан Z S 7 November 2014 в 05:27
поделиться

4 ответа

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;
}
9
ответ дан 27 November 2019 в 05:17
поделиться

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

Что-то вроде этого:

id DeepCopyViaArchiving(id<NSCoding> anObject)
{
    NSData* archivedData = [NSKeyedArchiver archivedDataWithRootObject:anObject];
    return [[NSKeyedUnarchiver unarchiveObjectWithData:archivedData] retain];
}

Но это не особенно эффективно.

11
ответ дан 27 November 2019 в 05:17
поделиться

Другой метод, который я видел (который совсем не очень эффективен), это использование объекта 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];
}

Опять же, это совсем не эффективно.

.
9
ответ дан 27 November 2019 в 05:17
поделиться

Благодаря бесплатному мосту можно также использовать функцию CoreFoundation CFPropertyListCreateDeepCopy:

NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
75
ответ дан 27 November 2019 в 05:17
поделиться