Різниця між objectForKey та valueForKey?


Відповіді:


403

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, то у вашому блозі Скотта Стівенсона є багато навчальних посібників . Ви також можете перевірити посилання на протокол NSKeyValueCoding .

Сподіваюся, що це допомагає.


12
valueForKey поводиться по-різному для об’єктів NSDictionary залежно від того, починається чи ключ із символу @.
dreamlax

61
objectForKey: приймає будь-який об’єкт як ключ, а не лише рядки. Єдина вимога - ключ підтримувати протокол копіювання NSC.
Ешлі Кларк

5
Я здивований, що ніхто не виправив цю відповідь, вказавши значенняForForKey: технічно не надає вам доступ до відповідної змінної екземпляра, а скоріше метод accessor, який (міг би) керувати змінною екземпляра.
Дані Джумаа

7
Попередження: valueForKey може бути дуже повільним - на даний момент це головне вузьке місце в моєму додатку iPad, настільки повільне, що його заміна на "стандартний" словник зробила додаток помітно швидшим. Щось дуже не так з KVC на iOS, і я більше ніколи його не буду використовувати - не варто падіння продуктивності, і все одно доведеться переписувати його довгий шлях. Для цього використовували ключі NSString зі значеннями NSString на CALayers. Інструменти показали, що "CAObject_valueForKey" становив 25% від загальної тривалості виконання (!)
Адам

2
@Adam Це звучить страшно. Ви пробували ще раз з iOS7? Якщо так, чи змінилися з того часу?
Unheilig

64

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

Насправді в документації зазначено, що навіть коли ви даєте valueForKey:NSString, він буде викликати objectForKey:все одно, якщо рядок не починається з @, у цьому випадку він викликає [super valueForKey:], що може викликати, valueForUndefinedKey:що може викликати виняток.


Ви можете, будь ласка, надіслати мені посилання документації, яку ви маєте на увазі. Дякую тобі.
Алі Амін

5
@ عليامين: Саме тут
dreamlax

20

Ось чудова причина використовувати, objectForKey:де це можливо, а не valueForKey:- valueForKey:невідомим ключем буде NSUnknownKeyExceptionвисловлюватися «цей клас не є ключовим значенням, що відповідає кодуванню ключа».


5
добре знати, що "valueForKey: з невідомим ключем викине NSUnknownKeyException, кажучи" цей клас не є ключовим значенням,
кодовим

Це просто не вірно з NSDictionary, і ви можете спробувати це: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey видаватиме такі винятки для інших класів, які не підтримують вказаний ключ - але для підкласів NSDictionary - ви просто отримаєте тихий нуль. спробуйте це:
Motti Shneor

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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.