2013-12-03 9 views
0

Я пытаюсь создать упорядоченный словарь в качестве подкласса NSMutableDictionary. Я использовал this article в качестве руководства, но я попытался выполнить свою собственную реализацию, прочитав документацию NSDictionary и NSMutableDictionary, что привело к тому, что я делал это немного иначе, чем в статье. А именно, аргумент для ключа setObject:forKey: должен быть id<NSCopying> (согласно документации), которого нет в статье.Не удается получить элементы из подкаталога NSMutableDictionary

Проблема заключается в том, когда я установил какое-то значение в setObject:forKey:, я не могу получить его надежно - это иногда найти объект для этого ключа, а иногда нет. Я не использую блоки или потоки или GCD где-нибудь в своем приложении, поэтому я не понимаю, как это может быть случайным образом.

Вот мой код:

// BBOrderedDictionary.h 
@interface BBOrderedDictionary : NSMutableDictionary 

@end 

// BBOrderedDictionary.m 
@interface BBOrderedDictionary() 

@property (nonatomic, strong) NSMutableArray* array; 
@property (nonatomic, strong) NSMutableDictionary* dictionary; 

@end 

@implementation BBOrderedDictionary 

-(instancetype)init 
{ 
    return [self initWithCapacity:0]; 
} 

-(instancetype)initWithCapacity:(NSUInteger)numItems 
{ 
    self = [super init]; 
    if (self) { 
     self.array = [NSMutableArray array]; 
     self.dictionary = [NSMutableDictionary dictionary]; 
    } 

    return self; 
} 

# pragma mark - Primitive Methods 

// NSMutableDictionary Primitive Methods 

-(void)setObject:(id)anObject forKey:(id<NSCopying>)aKey 
{ 
    if (![self.dictionary objectForKey:aKey]) { 
     [self.array addObject:aKey]; 
    } 
    [self.dictionary setObject:anObject forKey:aKey]; 
} 

-(void)removeObjectForKey:(id)aKey 
{ 
    [self.dictionary removeObjectForKey:aKey]; 
    [self.array removeObject:aKey]; 
} 

// NSDictionary Primitive Methods 

-(NSUInteger)count 
{ 
    return [self.dictionary count]; 
} 

-(id)objectForKey:(id)aKey 
{ 
    return [self.dictionary objectForKey:aKey]; 
} 

-(NSEnumerator *)keyEnumerator 
{ 
    return [self.array objectEnumerator]; 
} 

#pragma mark - Other 

-(NSArray *)allKeys 
{ 
    return self.array; 
} 

@end 

Следует отметить, что мои ключи пользовательского объекта, а также, что соответствует NSCopying и реализует isEqual:. Он отлично работает как ключ в NSMutableDictionary.

Когда я поставил точку останова в objectForKey:, я могу подтвердить, что внутренний словарь действительно есть все, что я вложил в нее, но (от сдачи журнала в isEqual: методе моего ключа класса), он не проверяет все свои ключи для равенства с ключевым аргументом. Есть идеи?

+3

Правильно ли вы использовали метод 'hash' в своем пользовательском классе, используемом в качестве ключей? Это требуется вместе с 'isEqual:'. – rmaddy

+0

Возможно, вы захотите проверить [Упорядоченный диктофон] (https://code.google.com/p/upnpx/source/browse/trunk/src/upnp/OrderedDictionary.h?spec=svn289&r=289) Мэтта Галлахера. Также [какао-сортированный словарь] (https://code.google.com/p/coco-sorted-dictionary/). – zaph

+0

@ Zaph: Статья, с которой я связан, был Мэтт Галлахер и рассказывает о причинах, лежащих в основе кода, с которым вы связались. – Shinigami

ответ

2

Когда вы реализуете собственный класс и реализуете метод isEqual:, вы также должны реализовать метод hash. для NSObject.

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

Обратите внимание, что два объекта, которые возвращают YES для isEqual:должны возвращать одинаковое значение для hash (который не используется по умолчанию). Однако два объекта, которые не равны (на основе isEqual:), могут иметь или не иметь то же значение hash.

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

3

NSDictionary - это не один класс, а кластер классов. NSDictionary - это публичный интерфейс к семейству различных частных классов, которые реализуют функциональность открытого интерфейса. Если вы хотите подклассифицировать кластер классов, вы должны реализовать все примитивные методы, поддерживающие открытый интерфейс. См. Документы Xcode в NSDictionary в разделе «Замечания о подклассе». Это скажет вам, что вам нужно делать, хотя я рекомендую против этого.

Кластерные кластеры часто выполняют специализированную оптимизацию под капотом, которую трудно подражать. Обычно лучше создать пользовательский объект, который управляет NSDictionary внутри, и предлагает некоторые из тех же методов, что и класс словаря (отношение «has-a», а не отношение «is-a». Иногда вы можете создать категорию для добавления функции в один из этих классов, хотя вы не можете переопределять методы базового класса в категории.

+0

Я ценю, что вы нашли время ответить, но я знаю все это (как показывают мои комментарии к коду и статья, с которой я связан). Кластеры классов подклассов обескуражены, но есть «правильный путь», который Apple предоставила, так что это еще не конец света. Моя реализация на самом деле просто декоратор, поэтому я не теряю оптимизацию, но подклассификация позволяет мне сохранять синтаксис легкого словаря [@ "key] вместо вызовов методов. Кроме того, я знаю, что есть альтернативы, но я пытаюсь узнать что-то, понимая, почему эта конкретная реализация не должна работать. – Shinigami

Смежные вопросы