Использование не копируемого объекта в качестве ключа для NSMutableDictionary? - PullRequest

Ответы [ 3 ]

9 голосов
/ 18 августа 2010

Вы абсолютно правы, это не хорошо.

Например, вы кодируете неправильный тип (это должно быть @encode(id), а не @encode(id*)), но в большинстве случаев это не должно вызывать больших проблем.

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

У вас есть два хороших варианта:

  1. Вы можете добавить NSCopying в класс или создать копируемый подкласс.

    • Эта опция будет работать только для объектов, которые могут быть реально скопированы. Это большинство классов, но не обязательно все (например, может быть плохо иметь несколько объектов, представляющих один и тот же входной поток)
    • Реализация копирования может быть проблемой даже для классов, где это имеет смысл - само по себе не сложно, но немного раздражает
  2. Вместо этого вы можете создать словарь с помощью CFDictionary API . Поскольку базовые типы типов не имеют универсальной функции копирования, CFDictionary просто сохраняет свои ключи по умолчанию (хотя вы можете настроить его поведение так, как вам нравится). Но CFDictionary также бесплатен и соединен с NSDictionary, что означает, что вы можете просто привести CFDictionaryRef к NSDictionary* (или NSMutableDictionary*) и затем обработать его как любой другой NSDictionary.

    • Это означает, что объект, который вы используете в качестве ключа, не должен изменяться (по крайней мере, таким образом, чтобы это не влияло на его значение hash), пока он находится в словаре - именно поэтому NSDictionary обычно требует этого скопировать его ключи
1 голос
/ 21 июля 2012

Для дальнейшего использования.

Теперь я знаю, что есть еще несколько вариантов.

  1. Переопределить методы в протоколе NSCopying и вернуть self вместо самого копирования. (вы должны сохранить его, если вы не используете ARC) Также вы гарантируете, что объект всегда будет возвращать одно и то же значение для метода -hash.

  2. Сделать копируемый простой контейнерный класс содержит строгую ссылку на исходный ключевой объект. Контейнер можно копировать, но он просто передает оригинальный ключ при копировании. Переопределить методы равенства / хеша также для соответствия семантике. Даже просто экземпляр NSArray, содержащий только ключевой объект, работает хорошо.

Метод № 1 выглядит довольно безопасно, но на самом деле я не уверен, что это безопасно. Потому что я не знаю внутреннее поведение NSDictionary. Поэтому я обычно использую путь № 2, который абсолютно безопасен в соглашении Какао.

Обновление

Теперь у нас есть NSHashTable и NSMapTable также в iOS начиная с версии 6.0.

0 голосов
/ 24 февраля 2012

Я не уверен на 100% в правильности этого решения, но выкладываю его на всякий случай.

Если вы не хотите использовать CFDictionary, возможно, вы могли бы использовать эту простую категорию:

@implementation NSMutableDictionary(NonCopyableKeys)
- (void)setObject:(id)anObject forNonCopyableKey:(id)aKey {
    [self setObject:anObject forKey:[NSValue valueWithPointer:aKey]];
}

- (id)objectForNonCopyableKey:(id)aKey {
    return [self objectForKey:[NSValue valueWithPointer:aKey]];
}

- (void)removeObjectForNonCopyableKey:(id)aKey {
    [self removeObjectForKey:[NSValue valueWithPointer:aKey]];
}
@end

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

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

Также убедитесь, что вы неt смешайте -(void)setObject:(id)anObject forNonCopyableKey:(id)aKey и - (id)objectForKey:(id)aKey методы, так как они не будут работать (последний вернет nil).

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

...