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

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

Ответ 1

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

valueForKey: является методом KVC. Он работает с ЛЮБЫМ классом. 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 Protocol.

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

Прежде всего, 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: отклоняется от стандарта от КВЦ документации, и не будет испускать 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);
}