Разница между objectForKey и valueForKey?

345

В чем разница между objectForKeyи valueForKey? Я посмотрел оба документа в документации, и они показались мне одинаковыми.

посвященный
источник

Ответы:

403

objectForKey:это NSDictionaryметод. An NSDictionaryявляется коллекционным классом, аналогичным классу an NSArray, за исключением того, что вместо использования индексов он использует ключи для различения элементов. Ключ - это произвольная строка, которую вы предоставляете. Никакие два объекта не могут иметь одинаковый ключ (точно так же, как никакие два объекта не NSArrayмогут иметь одинаковый индекс).

valueForKey:это метод КВК Это работает с любым классом. valueForKey:позволяет получить доступ к свойству, используя строку для его имени. Например, если у меня есть Accountкласс со свойством accountNumber, я могу сделать следующее:

NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

Используя KVC, я могу получить доступ к свойству динамически:

NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];

[newAccount setValue:anAccountNumber forKey:@"accountNumber"];

NSNumber *anotherAccountNumber = [newAccount valueForKey:@"accountNumber"];

Это эквивалентные наборы утверждений.

Я знаю, ты думаешь: вау, но саркастично. KVC выглядит не так уж и полезно. На самом деле это выглядит "многословно". Но когда вы хотите что-то изменить во время выполнения, вы можете сделать много интересных вещей, которые намного сложнее в других языках (но это выходит за рамки вашего вопроса).

Если вы хотите узнать больше о KVC, есть много учебников, если вы Google, особенно в блоге Скотта Стивенсона . Вы также можете проверить ссылку на протокол NSKeyValueCoding .

Надеюсь, это поможет.

Кори Флойд
источник
12
valueForKey ведет себя по-разному для объектов NSDictionary в зависимости от того, начинается ли ключ с символа @.
Dreamlax
61
objectForKey: принимает любой объект в качестве ключа, а не только строки. Единственное требование - чтобы ключ поддерживал протокол NSCopying.
Эшли Кларк
5
Я удивлен, что никто не исправил этот ответ, указав valueForKey: технически это не дает вам доступ к соответствующей переменной экземпляра, а скорее метод доступа, который (может) управлять переменной экземпляра.
Дани Джумаа
7
Предупреждение: valueForKey может быть очень медленным - в настоящее время оно является главным узким местом в моем приложении для iPad, настолько медленным, что замена его на «стандартный» словарь сделала приложение заметно быстрее. Что-то очень не так с KVC на iOS, и я никогда не буду использовать его снова - не стоит снижения производительности и необходимости переписывать его в любом случае. Это использовало ключи NSString со значениями NSString на CALayers. Инструменты показали, что «CAObject_valueForKey» составлял 25% от общего времени выполнения (!)
Адам
2
@ Адам Это звучит страшно. Вы пробовали это снова с iOS7? Если так, изменились ли вещи с тех пор?
Unheilig
64

Когда вам это valueForKey:нужно, вам нужно дать ему NSString, тогда как вы objectForKey:можете взять любой подкласс NSObject в качестве ключа. Это потому, что для кодирования значения ключа ключи всегда являются строками.

Фактически, в документации говорится, что даже когда вы передаете valueForKey:NSString, она все objectForKey:равно будет вызываться, если строка не начинается с @, в этом случае она вызывает [super valueForKey:], что может вызвать вызов, valueForUndefinedKey:который может вызвать исключение.

dreamlax
источник
Можете ли вы дать мне ссылку на документацию, которую вы имеете в виду. Спасибо.
Али Амин
5
@ عليامين: Это прямо здесь
Dreamlax
20

Вот отличная причина, чтобы использовать objectForKey:везде, где это возможно, вместо valueForKey:- valueForKey:с неизвестным ключом будет выброшено NSUnknownKeyExceptionвысказывание «этот класс не соответствует значению ключа для кода».

Ник Локинг
источник
5
Полезно знать, что «valueForKey: с неизвестным ключом вызовет исключение NSUnknownKeyException, сказав, что« этот класс не соответствует значению ключа для кода »
onmyway133
Это просто неверно с NSDictionary, и вы можете попробовать это: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey будет генерировать такие исключения для других классов, которые не поддерживают указанный ключ - но для подклассов NSDictionary - вы просто получите тихий ноль. попробуйте это:
Мотти Шнеор
13

Как уже говорилось, objectForKey:тип данных - :(id)aKeyэто valueForKey:тип данных :(NSString *)key.

Например:

 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:@"123"],[NSNumber numberWithInteger:5], nil];

 NSLog(@"objectForKey : --- %@",[dict objectForKey:[NSNumber numberWithInteger:5]]);  
    //This will work fine and prints (    123    )  

 NSLog(@"valueForKey  : --- %@",[dict valueForKey:[NSNumber numberWithInteger:5]]); 
    //it gives warning "Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'"   ---- This will crash on runtime. 

Таким образом, valueForKey:будет принимать только строковое значение и является методом KVC, тогда как objectForKey:будет принимать любой тип объекта.

Значение в objectForKeyбудет доступно для объекта того же типа.

Харджот Сингх
источник
0

Я постараюсь дать исчерпывающий ответ здесь. Большая часть пунктов появляется в других ответах, но я нашел каждый ответ неполным, а некоторые неправильным.

Прежде всего, objectForKey:это NSDictionaryметод, а valueForKey:метод протокола KVC требуется для любого класса жалоб KVC, включая NSDictionary.

Кроме того, как писал @dreamlax, документация намекает на то, что NSDictionaryего valueForKey:метод использует ИСПОЛЬЗОВАНИЕ его objectForKey:реализации. Другими словами - [NSDictionary valueForKey:]звонки [NSDictionary objectForKey:].

Это подразумевает, что valueForKey:это никогда не может быть быстрее, чем objectForKey:(на одном и том же ключе ввода), хотя тщательное тестирование, которое я провел, подразумевает разницу от 5% до 15%, более миллиардов случайного доступа к огромному NSDictionary. В нормальных ситуациях - разница незначительна.

Далее: протокол KVC работает только с NSString *ключами, следовательно, valueForKey:будет принимать только NSString *(или подкласс) в качестве ключа, в то время как NSDictionaryможет работать с другими типами объектов в качестве ключей - так что «нижний уровень» objectForKey:принимает любой объект с возможностью копирования (совместимый с протоколом NSCopying) в качестве ключа.

Наконец, NSDictionary'sреализация valueForKey:отклоняется от стандартного поведения, определенного в документации KVC, и НЕ будет выдавать NSUnknownKeyExceptionключ, который не может найти - если только это не «специальный» ключ, начинающийся с «@», что обычно означает « агрегация "функциональная клавиша (например @"@sum, @"@avg"). Вместо этого он просто вернет ноль, когда ключ не найден в NSDictionary - ведя себя так же, какobjectForKey:

Ниже приведен тестовый код для демонстрации и подтверждения моих заметок.

- (void) dictionaryAccess {
    NSLog(@"Value for Z:%@", [@{@"X":@(10), @"Y":@(20)} valueForKey:@"Z"]); // prints "Value for Z:(null)"

    uint32_t testItemsCount = 1000000;
    // create huge dictionary of numbers
    NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:testItemsCount];
    for (long i=0; i<testItemsCount; ++i) {
        // make new random key value pair:
        NSString *key = [NSString stringWithFormat:@"K_%u",arc4random_uniform(testItemsCount)];
        NSNumber *value = @(arc4random_uniform(testItemsCount));
        [d setObject:value forKey:key];
    }
    // create huge set of random keys for testing.
    NSMutableArray *keys = [NSMutableArray arrayWithCapacity:testItemsCount];
    for (long i=0; i<testItemsCount; ++i) {
        NSString *key = [NSString stringWithFormat:@"K_%u",arc4random_uniform(testItemsCount)];
        [keys addObject:key];
    }

    NSDictionary *dict = [d copy];
    NSTimeInterval vtotal = 0.0, ototal = 0.0;

    NSDate *start;
    NSTimeInterval elapsed;

    for (int i = 0; i<10; i++) {

        start = [NSDate date];
        for (NSString *key in keys) {
            id value = [dict valueForKey:key];
        }
        elapsed = [[NSDate date] timeIntervalSinceDate:start];
        vtotal+=elapsed;
        NSLog (@"reading %lu values off dictionary via valueForKey took: %10.4f seconds", keys.count, elapsed);

        start = [NSDate date];
        for (NSString *key in keys) {
            id obj = [dict objectForKey:key];
        }
        elapsed = [[NSDate date] timeIntervalSinceDate:start];
        ototal+=elapsed;
        NSLog (@"reading %lu objects off dictionary via objectForKey took: %10.4f seconds", keys.count, elapsed);
    }

    NSString *slower = (vtotal > ototal) ? @"valueForKey" : @"objectForKey";
    NSString *faster = (vtotal > ototal) ? @"objectForKey" : @"valueForKey";
    NSLog (@"%@ takes %3.1f percent longer then %@", slower, 100.0 * ABS(vtotal-ototal) / MAX(ototal,vtotal), faster);
}
Мотти Шнеор
источник