Есть ли способ перебора словаря?

Я знаю NSDictionaries как то, где вам нужен key, чтобы получить value. Но как я могу перебирать все keys и values в NSDictionary, чтобы я знал, какие ключи есть и какие значения есть? Я знаю, что есть что-то, называемое for-in-loop в JavaScript. Есть ли что-то подобное в Objective-C?

Ответ 1

Да, NSDictionary поддерживает быстрое перечисление. С помощью Objective-C 2.0 вы можете сделать это:

// To print out all key-value pairs in the NSDictionary myDict
for(id key in myDict)
    NSLog(@"key=%@ value=%@", key, [myDict objectForKey:key]);

Альтернативный метод (который вы должны использовать, если вы ориентируетесь на Mac OS X до 10.5, но вы все еще можете использовать на 10.5 и iPhone) заключается в использовании NSEnumerator:

NSEnumerator *enumerator = [myDict keyEnumerator];
id key;
// extra parens to suppress warning about using = instead of ==
while((key = [enumerator nextObject]))
    NSLog(@"key=%@ value=%@", key, [myDict objectForKey:key]);

Ответ 2

Блочный подход позволяет избежать выполнения алгоритма поиска для каждого ключа:

[dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) {
  NSLog(@"%@ => %@", key, value);
}];

Несмотря на то, что NSDictionary реализован как hashtable (это означает, что стоимость поиск элемента O(1)), поисковые запросы по-прежнему замедляют вашу итерацию на постоянный коэффициент.

Мои измерения показывают, что для словаря d чисел...

NSMutableDictionary* dict = [NSMutableDictionary dictionary];
for (int i = 0; i < 5000000; ++i) {
  NSNumber* value = @(i);
  dict[value.stringValue] = value;
}

... суммируя числа с блочным подходом...

__block int sum = 0;
[dict enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSNumber* value, BOOL* stop) {
  sum += value.intValue;
}];

... а не петлевой подход...

int sum = 0;
for (NSString* key in dict)
  sum += [dict[key] intValue];

... примерно на 40% быстрее.

EDIT. Новый SDK (6.1+), по-видимому, оптимизирует итерацию цикла, поэтому подход к петле теперь примерно на 20% быстрее, чем блок-подход, по крайней мере для простой пример выше.

Ответ 3

Это итерация с использованием блочного подхода:

    NSDictionary *dict = @{@"key1":@1, @"key2":@2, @"key3":@3};

    [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        NSLog(@"%@->%@",key,obj);
        // Set stop to YES when you wanted to break the iteration.
    }];

С автозаполнением очень быстро устанавливается, и вам не нужно беспокоиться о записи итерационного конверта.