Разница между 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);
}