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

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

6 ответов

Решение

objectForKey: является NSDictionary метод. NSDictionary это класс коллекции, похожий на 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.

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

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

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

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

Как сказано, 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 будет доступен тот же тип объекта.

В этой таблице представлены четыре различия между objectForKey а также valueForKey.

      //   |---------------------|-------------------------|------------------------|
//   |                     |      `objectForKey`     |      `valueForKey`     |
//   |---------------------|-------------------------|------------------------|
//   | Works on ...        |       NSDictionary      |   NSDictionary / KVC   |
//   |---------------------|-------------------------|------------------------|
//   | Throws exception    |            No           |       Yes (on KVC)     |
//   |---------------------|-------------------------|------------------------|
//   | Feed                |   NSObject's subclass   |        NSString        |
//   |---------------------|-------------------------|------------------------|
//   | Usage on KVC        |          cannot         |           can          |
//   |---------------------|-------------------------|------------------------|

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

Прежде всего, 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"). Вместо этого он просто вернет nil, если ключ не найден в 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);
}
Другие вопросы по тегам