Różnica między objectForKey i valueForKey?

Jaka jest różnica między objectForKey a valueForKey? Sprawdziłem oba w dokumentacji i wydawali mi się tacy sami.


person Devoted    schedule 30.06.2009    source źródło


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.

person Corey Floyd    schedule 30.06.2009
comment
valueForKey zachowuje się inaczej dla obiektów NSDictionary w zależności od tego, czy klucz zaczyna się od symbolu @. - person dreamlax; 30.06.2009
comment
objectForKey: akceptuje dowolny obiekt jako klucz, a nie tylko ciągi. Jedynym wymaganiem jest, aby klucz obsługiwał protokół NSCopying. - person Ashley Clark; 30.06.2009
comment
Oba te punkty są zdecydowanie prawdziwe. NSDictionaries to szczególny przypadek i nie musisz używać łańcuchów dla kluczy. Tak naprawdę nie zagłębiałem się w subtelności, ponieważ wydaje się, że pytający wymagał tylko uproszczonego porównania. - person Corey Floyd; 30.06.2009
comment
Jestem zaskoczony, że nikt nie poprawił tej odpowiedzi, wskazując valueForKey: technicznie nie daje dostępu do odpowiedniej zmiennej instancji, ale raczej metodę akcesora, która (może) zarządzać zmienną instancji. - person Dany Joumaa; 25.10.2012
comment
Ostrzeżenie: valueForKey może być bardzo powolna – jest to obecnie główne wąskie gardło w mojej aplikacji na iPada, tak wolne, że zastąpienie go standardowym słownikiem sprawiło, że aplikacja była zauważalnie szybsza. Coś bardzo nie tak z KVC na iOS i nigdy więcej go nie użyję – nie warte spadku wydajności i konieczności przepisywania go na dłuższą metę. To było przy użyciu kluczy NSString z wartościami NSString na CALayers. Instrumenty wykazały, że CAObject_valueForKey stanowił 25% całkowitego czasu pracy (!) - person Adam; 04.01.2013
comment
@Adam To brzmi przerażająco. Czy próbowałeś go ponownie od iOS7? Jeśli tak, czy od tego czasu coś się zmieniło? - person Unheilig; 14.03.2014
comment
Czasami jednak pojawia się praktyczna różnica, której jeszcze jasno nie rozumiem. 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
comment
@Adam Myślę, że się pomyliłeś. Kiedy go instrumentujesz - musisz odróżnić czas, jaki zajmuje CAObject_valueForKey, od czasu, w którym CALayer faktycznie pobiera wartość. Protokół KVC jest synchroniczny, więc jeśli CALayer musi coś zablokować przed udzieleniem odpowiedzi - ogólna wydajność może wydawać się niska, nawet jeśli sam valueForKey jest bardzo szybki. KVC jest niesamowicie szybki i trwa od czasu wprowadzenia. Nie ma nic wspólnego z iOS/tvOS/MacOS --- to jest głęboko w środowisku uruchomieniowym ObjC. Zapewnia również środki do robienia cudownych rzeczy, które w przeciwnym razie byłyby niemożliwe. - person Motti Shneor; 04.07.2019

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.

person dreamlax    schedule 30.06.2009
comment
czy możesz podać mi link do dokumentacji, którą masz na myśli. Dziękuję Ci. - person Ali Amin; 23.01.2014
comment
@عليامين: To właśnie tutaj - person dreamlax; 24.01.2014

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”.

person Nick Locking    schedule 30.12.2013
comment
dobrze wiedzieć, że valueForKey: z nieznanym kluczem wyrzuci NSUnknownKeyException, mówiąc, że ta klasa nie jest zgodna z kodowaniem wartości klucza dla klucza - person onmyway133; 15.08.2014
comment
To po prostu nie jest prawdą w przypadku NSDictionary i możesz spróbować tego:NSLog(@Z:%@, [@{@X:@(10), @Y:@(20)} valueForKey:@Z]) ; valueForKey wyemituje takie wyjątki dla innych klas, które nie obsługują określonego klucza - ale dla podklas NSDictionary - otrzymasz po prostu ciche zero. Spróbuj tego: - person Motti Shneor; 04.07.2019

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.

person Harjot Singh    schedule 17.07.2014

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);
}
person Motti Shneor    schedule 04.07.2019

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          |
//   |---------------------|-------------------------|------------------------|
person Andy Fedoroff    schedule 06.07.2021