Różnica między objectForKey a valueForKey?

345

Jaka jest różnica między objectForKeyi valueForKey? Zajrzałem do obu dokumentów i wydawały mi się takie same.

Oddany
źródło

Odpowiedzi:

403

objectForKey:jest NSDictionarymetodą. An NSDictionaryjest klasą kolekcji podobną do tej NSArray, z tą różnicą, że zamiast indeksów używa kluczy do rozróżniania elementów. Klucz to dowolny ciąg, który podajesz. Żadne dwa obiekty nie mogą mieć tego samego klucza (podobnie jak żadne dwa obiekty w jednym nie NSArraymogą mieć tego samego indeksu).

valueForKey:jest metodą KVC. Działa z KAŻDĄ klasą. valueForKey:umożliwia dostęp do właściwości za pomocą ciągu znaków dla jej nazwy. Na przykład, jeśli mam Accountklasę 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 nieruchomości:

NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];

[newAccount setValue:anAccountNumber forKey:@"accountNumber"];

NSNumber *anotherAccountNumber = [newAccount valueForKey:@"accountNumber"];

Są to równoważne zestawy instrukcji.

Wiem, że myślisz: wow, ale sarkastycznie. KVC nie wygląda na tak przydatne. W rzeczywistości wygląda „wordy”. Ale jeśli chcesz zmienić rzeczy w czasie wykonywania, możesz zrobić wiele fajnych rzeczy, które są znacznie trudniejsze w innych językach (ale to jest poza zakresem twojego pytania).

Jeśli chcesz dowiedzieć się więcej o KVC, istnieje wiele samouczków, jeśli Google, szczególnie na blogu Scotta Stevensona . Możesz także sprawdzić NSKeyValueCoding Protocol Reference .

Mam nadzieję, że to pomaga.

Corey Floyd
źródło
12
wartośćForKey zachowuje się inaczej dla obiektów NSDictionary w zależności od tego, czy klucz zaczyna się od symbolu @.
dreamlax
61
objectForKey: akceptuje dowolny obiekt jako klucz, a nie tylko ciągi znaków. Jedynym wymaganiem jest to, aby klucz obsługiwał protokół NSCopying.
Ashley Clark
5
Dziwię się, że nikt nie poprawił tej odpowiedzi, wskazując wartośćForKey: technicznie nie daje ci dostępu do odpowiedniej zmiennej instancji, ale raczej metodę akcesorium, która (mogłaby) zarządzać zmienną instancji.
Dany Joumaa,
7
Ostrzeżenie: valueForKey może być bardzo powolny - jest to obecnie główne wąskie gardło w mojej aplikacji na iPada, tak powolne, że zastąpienie go „standardowym” słownikiem znacznie przyspieszyło aplikację. Coś jest bardzo nie tak z KVC na iOS, i nigdy więcej go nie użyję - nie jest to warte spadku wydajności, i i tak muszę go ponownie pisać. Używano kluczy NSString z wartościami NSString na CALayers. Instrumenty wykazały, że „CAObject_valueForKey” stanowi 25% całkowitego czasu działania (!)
Adam
2
@Adam To brzmi przerażająco. Czy próbowałeś jeszcze raz od iOS7? Jeśli tak, to czy od tego czasu się zmieniło?
Unheilig
64

Gdy to zrobisz valueForKey:, musisz nadać mu NSString, podczas gdy objectForKey:możesz wziąć dowolną podklasę NSObject jako klucz. Jest tak, ponieważ w przypadku kodowania klucz-wartość klucze są zawsze łańcuchami.

W rzeczywistości dokumentacja stwierdza, że ​​nawet jeśli podasz valueForKey:NSString, i tak będzie on wywoływał, objectForKey:chyba że łańcuch zaczyna się od @, w którym to przypadku wywołuje [super valueForKey:], co może wywoływać valueForUndefinedKey:wyjątek.

dreamlax
źródło
czy możesz podać mi link do dokumentacji, którą masz na myśli. Dziękuję Ci.
Ali Amin
5
@ عليامين: To jest tutaj
dreamlax
20

Oto świetny powód, aby używać objectForKey:wszędzie tam, gdzie to możliwe zamiast valueForKey:- valueForKey:z nieznanym kluczem rzuci się NSUnknownKeyExceptionpowiedzenie „ta klasa nie jest zgodna z kodowaniem wartości klucza dla klucza”.

Nick Locking
źródło
5
dobrze wiedzieć, że „wartośćForKey: z nieznanym kluczem spowoduje zgłoszenie wyjątku NSUnknownKeyException mówiąc„ ta klasa nie jest zgodna z kodowaniem wartości klucza dla klucza ”
onmyway133 15.08.2014
Jest to po prostu nieprawda w przypadku NSDictionary i możesz spróbować: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ „Z”]); valueForKey wyśle ​​takie wyjątki na inne klasy, które nie obsługują określonego klucza - ale dla podklas NSDictionary - otrzymasz po prostu cichy zero. spróbuj tego:
Motti Shneor
13

Jak powiedziano, objectForKey:typem danych jest :(id)aKeynatomiast valueForKey:typ danych :(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:weźmie tylko wartość ciągu i jest metodą KVC, podczas gdy objectForKey:zabierze dowolny typ obiektu.

objectForKeyDostęp do wartości będzie uzyskiwał ten sam rodzaj obiektu.

Harjot Singh
źródło
0

Spróbuję udzielić wyczerpującej odpowiedzi tutaj. Wiele punktów pojawia się w innych odpowiedziach, ale okazało się, że każda odpowiedź jest niepełna, a niektóre błędne.

Przede wszystkim objectForKey:jest NSDictionarymetodą, podczas gdy valueForKey:jest metodą protokołu KVC wymaganą od dowolnej klasy skarg KVC - w tym NSDictionary.

Ponadto, jak @dreamlax napisał, podpowiedzi dokumentacji, która NSDictionaryrealizuje swoją valueForKey:metodę UŻYWAĆ jego objectForKey:realizację. Innymi słowy - [NSDictionary valueForKey:]wzywa [NSDictionary objectForKey:].

Oznacza to, że valueForKey:nigdy nie może być szybsze niż objectForKey:(przy tym samym kluczu wejściowym), chociaż dokładne testy, które przeprowadziłem, wskazują na około 5% do 15% różnicy, w porównaniu z miliardami losowego dostępu do ogromnego NSDictionary. W normalnych sytuacjach różnica jest znikoma.

Dalej: protokół KVC działa tylko z NSString *kluczami, dlatego valueForKey:akceptuje tylko NSString *klucz (lub podklasę) jako klucz, podczas gdy NSDictionarymoże pracować z innymi rodzajami obiektów jako kluczami - dzięki czemu „niższy poziom” objectForKey:akceptuje dowolny obiekt, który można kopiować (zgodny z protokołem NSCopying) jako klucz.

Wreszcie, NSDictionary'simplementacja valueForKey:odbiega od standardowego zachowania zdefiniowanego w dokumentacji KVC i NIE wyda NSUnknownKeyExceptionklucza, którego nie może znaleźć - chyba że jest to klucz „specjalny” - taki, który zaczyna się od „@” - co zwykle oznacza „ agregacja ”klawisz funkcyjny (np @"@sum, @"@avg".). Zamiast tego zwróci zero, gdy klucz nie zostanie znaleziony w NSDictionary - zachowując się tak samo jakobjectForKey:

Poniżej znajduje się kod testowy do zademonstrowania i potwierdzenia 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);
}
Motti Shneor
źródło