Jaka jest różnica między objectForKey
a valueForKey
? Sprawdziłem oba w dokumentacji i wydawali mi się tacy sami.
Różnica między objectForKey i valueForKey?
Odpowiedzi (6)
objectForKey:
to metoda NSDictionary
. NSDictionary
to klasa kolekcji podobna do NSArray
, z tą różnicą, że zamiast indeksów używa kluczy do rozróżniania elementów. Klucz to dowolny ciąg znaków, który podajesz. Żadne dwa obiekty nie mogą mieć tego samego klucza (tak jak żadne dwa obiekty w NSArray
nie mogą mieć tego samego indeksu).
valueForKey:
to metoda KVC. Działa z KAŻDĄ klasą. valueForKey:
umożliwia dostęp do właściwości przy użyciu ciągu znaków w nazwie. Na przykład, jeśli mam klasę Account
z właściwością accountNumber
, mogę wykonać następujące czynności:
NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];
[newAccount setAccountNumber:anAccountNUmber];
NSNumber *anotherAccountNumber = [newAccount accountNumber];
Korzystając z KVC, mogę uzyskać dynamiczny dostęp do właściwości:
NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];
[newAccount setValue:anAccountNumber forKey:@"accountNumber"];
NSNumber *anotherAccountNumber = [newAccount valueForKey:@"accountNumber"];
To są równoważne zestawy stwierdzeń.
Wiem, że myślisz: wow, ale sarkastycznie. KVC nie wygląda aż tak użytecznie. W rzeczywistości wygląda na „przegadaną”. Ale kiedy chcesz coś zmienić w czasie wykonywania, możesz zrobić wiele fajnych rzeczy, które są znacznie trudniejsze w innych językach (ale to wykracza poza zakres twojego pytania).
Jeśli chcesz dowiedzieć się więcej o KVC, możesz skorzystać z wielu samouczków, zwłaszcza na blogu Scotta Stevensona. Możesz również sprawdzić Dokumentacja protokołu NSKeyValueCoding.
Mam nadzieję, że to pomoże.
valueForKey
nie działało w kodzie SOGo-3.1.4, próbując wywołać niedostępną metodę ASProtocolVersion
na obiekcie context
(EXCEPTION: <NSException: 0x55f43f93e4d0> NAME:NSInvalidArgumentException REASON:-[WOContext ASProtocolVersion]: unrecognized selector sent to instance
), podczas gdy objectForKey
działa (i jest to typowy sposób zapytania o obiekt context
w innym miejscu kodu). Zobacz github.com/inverse-inc/sogo/pull/217/files
- person imz -- Ivan Zakharyaschev; 20.07.2016
Kiedy to zrobisz, valueForKey:
musisz nadać mu NSString, podczas gdy objectForKey:
może przyjąć dowolną podklasę NSObject jako klucz. Dzieje się tak, ponieważ w przypadku kodowania klucz-wartość klucze są zawsze ciągami.
W rzeczywistości dokumentacja stwierdza, że nawet jeśli podasz valueForKey:
ciąg NSString, i tak wywoła on objectForKey:
, chyba że ciąg zaczyna się od @
, w którym to przypadku wywołuje [super valueForKey:]
, co może wywołać valueForUndefinedKey:
, co może zgłosić wyjątek.
Oto doskonały powód, aby używać objectForKey:
wszędzie tam, gdzie to możliwe zamiast valueForKey:
- valueForKey:
z nieznanym kluczem, spowoduje wyświetlenie NSUnknownKeyException
mówiącego „ta klasa nie jest zgodna z kodowaniem wartości klucza dla klucza”.
Jak już powiedziano, typ danych objectForKey:
to :(id)aKey
, podczas gdy typ danych valueForKey:
to :(NSString *)key
.
Na przykład:
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.
Tak więc valueForKey:
przyjmie tylko wartość ciągu i jest metodą KVC, podczas gdy objectForKey:
przyjmie dowolny typ obiektu.
Do wartości w objectForKey
będzie miał dostęp ten sam rodzaj obiektu.
Postaram się tutaj udzielić wyczerpującej odpowiedzi. Wiele punktów pojawia się w innych odpowiedziach, ale każda odpowiedź jest dla mnie niekompletna, a niektóre niepoprawne.
Przede wszystkim objectForKey:
jest metodą NSDictionary
, natomiast valueForKey:
jest metodą protokołu KVC wymaganą od każdej klasy skarg KVC - w tym NSDictionary.
Ponadto, jak napisał @dreamlax, dokumentacja wskazuje, że NSDictionary
implementuje swoją metodę valueForKey:
UŻYWA swojej implementacji objectForKey:
. Innymi słowy - [NSDictionary valueForKey:]
dzwoni na [NSDictionary objectForKey:]
.
Oznacza to, że valueForKey:
nigdy nie może być szybsze niż objectForKey:
(na tym samym kluczu wejściowym), chociaż dokładne testy, które przeprowadziłem, wskazują na około 5% do 15% różnicy, ponad miliardy losowego dostępu do ogromnego słownika NSD. W normalnych sytuacjach różnica jest znikoma.
Dalej: Protokół KVC działa tylko z NSString *
kluczami, stąd valueForKey:
zaakceptuje tylko NSString *
(lub podklasę) jako klucz, podczas gdy NSDictionary
może pracować z innymi rodzajami obiektów jako kluczami - tak, że "niższy poziom" objectForKey:
akceptuje dowolne kopiowalne ( zgodny z protokołem NSCopying) jako klucz.
Wreszcie, NSDictionary's
implementacja valueForKey:
odbiega od standardowego zachowania zdefiniowanego w dokumentacji KVC i NIE wyemituje NSUnknownKeyException
dla klucza, którego nie może znaleźć - chyba że jest to "specjalny" klucz - taki, który zaczyna się od "@" - który zwykle oznacza klawisz funkcyjny „agregacja” (np. @"@sum, @"@avg"
). Zamiast tego po prostu zwróci zero, gdy klucz nie zostanie znaleziony w NSDictionary - zachowując się tak samo jak objectForKey:
Poniżej znajduje się kod testowy do zademonstrowania i udowodnienia moich notatek.
- (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);
}
Ta tabela przedstawia cztery różnice między objectForKey
a valueForKey
.
// |---------------------|-------------------------|------------------------|
// | | `objectForKey` | `valueForKey` |
// |---------------------|-------------------------|------------------------|
// | Works on ... | NSDictionary | NSDictionary / KVC |
// |---------------------|-------------------------|------------------------|
// | Throws exception | No | Yes (on KVC) |
// |---------------------|-------------------------|------------------------|
// | Feed | NSObject's subclass | NSString |
// |---------------------|-------------------------|------------------------|
// | Usage on KVC | cannot | can |
// |---------------------|-------------------------|------------------------|