Differenza tra objectForKey e valueForKey?


345

Qual è la differenza tra objectForKeye valueForKey? Ho cercato entrambi nella documentazione e mi sono sembrati uguali.

Risposte:


403

objectForKey:è un NSDictionarymetodo. An NSDictionaryè una classe di raccolta simile a an NSArray, tranne che invece di utilizzare gli indici, utilizza le chiavi per differenziare gli elementi. Una chiave è una stringa arbitraria che fornisci. Non ci sono due oggetti che possono avere la stessa chiave (così come non ci sono due oggetti in uno NSArraypuò avere lo stesso indice).

valueForKey:è un metodo KVC. Funziona con QUALSIASI classe. valueForKey:ti permette di accedere a una proprietà usando una stringa per il suo nome. Ad esempio, se ho una Accountclasse con una proprietà accountNumber, posso fare quanto segue:

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

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

Utilizzando KVC, posso accedere alla proprietà in modo dinamico:

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

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

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

Quelle sono serie equivalenti di dichiarazioni.

So che stai pensando: wow, ma sarcasticamente. KVC non sembra molto utile. In effetti, sembra "prolisso". Ma quando vuoi cambiare le cose in fase di esecuzione, puoi fare molte cose interessanti che sono molto più difficili in altre lingue (ma questo va oltre lo scopo della tua domanda).

Se vuoi saperne di più su KVC, ci sono molti tutorial su Google, in particolare sul blog di Scott Stevenson . Puoi anche consultare il riferimento al protocollo NSKeyValueCoding .

Spero che aiuti.


12
valueForKey si comporta diversamente per gli oggetti NSDictionary a seconda che la chiave inizi con un simbolo @.
dreamlax,

61
objectForKey: accetta qualsiasi oggetto come chiave, non solo stringhe. L'unico requisito è che la chiave supporti il ​​protocollo NSCopying.
Ashley Clark,

5
Sono sorpreso che nessuno abbia corretto questa risposta sottolineando valueForKey: tecnicamente non ti dà accesso alla corrispondente variabile di istanza, ma piuttosto al metodo di accesso che (potrebbe) gestire la variabile di istanza.
Dany Joumaa,

7
Attenzione: valueForKey può essere molto lento - attualmente è un grosso collo di bottiglia nella mia app per iPad, così lento che sostituirlo con un dizionario "standard" ha reso l'app notevolmente più veloce. Qualcosa di molto sbagliato in KVC su iOS, e non lo userò mai più - non vale il calo delle prestazioni e devo comunque riscriverlo a lungo. Questo utilizzava chiavi NSString con valori NSString su CALayer. Gli strumenti hanno mostrato che "CAObject_valueForKey" era il 25% del tempo di esecuzione totale (!)
Adam

2
@Adam Sembra spaventoso. Hai provato di nuovo da iOS7? Se è così, le cose sono cambiate da allora?
Unheilig

64

Quando lo fai valueForKey:devi dargli una NSString, mentre objectForKey:può prendere qualsiasi sottoclasse NSObject come chiave. Questo perché per la codifica valore-chiave, le chiavi sono sempre stringhe.

In effetti, la documentazione afferma che anche quando si dà valueForKey:un NSString, invocherà objectForKey:comunque a meno che la stringa non inizi con un @, nel qual caso invoca [super valueForKey:], che può chiamare valueForUndefinedKey:che può sollevare un'eccezione.


puoi per favore darmi il link della documentazione che intendi. grazie.
Ali Amin,

5
@ عليامين: È proprio qui
dreamlax,

20

Ecco un ottimo motivo per usarlo objectForKey:laddove possibile anziché valueForKey:- valueForKey:con una chiave sconosciuta verrà NSUnknownKeyExceptiondetto "questa classe non è conforme alla codifica del valore chiave per la chiave".


5
buono a sapersi che "valueForKey: con una chiave sconosciuta genererà NSUnknownKeyException dicendo" questa classe non è conforme alla codifica del valore chiave per la chiave "
onmyway133

Questo semplicemente non è vero con NSDictionary e sei il benvenuto a provare questo: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey emetterà tali eccezioni su altre classi che non supportano una chiave specificata, ma per le sottoclassi di NSDictionary, riceverai semplicemente un valore zero. prova questo:
Motti Shneor il

13

Come detto, il objectForKey:tipo di dati è :(id)aKeymentre il valueForKey:tipo di dati è :(NSString *)key.

Per esempio:

 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. 

Quindi, valueForKey:prenderà solo un valore stringa ed è un metodo KVC, mentre objectForKey:prenderà qualsiasi tipo di oggetto.

Il valore in objectForKeysarà accessibile dallo stesso tipo di oggetto.


0

Proverò a fornire una risposta completa qui. Gran parte dei punti appaiono in altre risposte, ma ho trovato ciascuna risposta incompleta e alcune errate.

Innanzitutto, objectForKey:è un NSDictionarymetodo, mentre valueForKey:è richiesto un metodo di protocollo KVC per qualsiasi classe di reclamo KVC, incluso NSDictionary.

Inoltre, come ha scritto @dreamlax, la documentazione suggerisce che NSDictionaryimplementa il suo valueForKey:metodo USANDO la sua objectForKey:implementazione. In altre parole: [NSDictionary valueForKey:]chiama [NSDictionary objectForKey:].

Ciò implica che ciò valueForKey:non potrà mai essere più veloce di objectForKey:(sulla stessa chiave di input) anche se test approfonditi che ho fatto implicano una differenza dal 5% al ​​15% circa, oltre miliardi di accessi casuali a un enorme NSDictionary. In situazioni normali - la differenza è trascurabile.

Next: Il protocollo KVC funziona solo con le NSString *chiavi, quindi valueForKey:accetta solo una NSString *(o sottoclasse) come chiave, mentre NSDictionarypuò funzionare con altri tipi di oggetti come chiavi - in modo che il "livello inferiore" objectForKey:accetti qualsiasi oggetto in grado di copiare (conforme al protocollo NSCopying) come chiave.

Infine, l' NSDictionary'simplementazione di valueForKey:deviazioni dal comportamento standard definito nella documentazione di KVC e NON emetterà una NSUnknownKeyExceptionper una chiave che non riesce a trovare - a meno che questa non sia una chiave "speciale" - quella che inizia con "@" - che di solito significa un " tasto funzione "aggregazione" (ad es @"@sum, @"@avg".). Al contrario, restituirà semplicemente uno zero quando non viene trovata una chiave in NSDictionary - comportandosi allo stesso modo diobjectForKey:

Di seguito è riportato un codice di prova per dimostrare e dimostrare le mie note.

- (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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.