Unterschied zwischen objectForKey und valueForKey?

345

Was ist der Unterschied zwischen objectForKeyund valueForKey? Ich habe beide in der Dokumentation nachgeschlagen und sie schienen mir gleich zu sein.

Hingebungsvoll
quelle

Antworten:

403

objectForKey:ist eine NSDictionaryMethode. An NSDictionaryist eine Auflistungsklasse ähnlich einer NSArray, außer dass anstelle von Indizes Schlüssel zur Unterscheidung zwischen Elementen verwendet werden. Ein Schlüssel ist eine beliebige Zeichenfolge, die Sie angeben. Keine zwei Objekte können denselben Schlüssel haben (so wie keine zwei Objekte in einem NSArraydenselben Index haben können).

valueForKey:ist eine KVC-Methode. Es funktioniert mit jeder Klasse. valueForKey:Ermöglicht den Zugriff auf eine Eigenschaft mithilfe einer Zeichenfolge für ihren Namen. Wenn ich beispielsweise eine AccountKlasse mit einer Eigenschaft habe accountNumber, kann ich Folgendes tun:

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

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

Mit KVC kann ich dynamisch auf die Eigenschaft zugreifen:

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

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

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

Dies sind äquivalente Sätze von Anweisungen.

Ich weiß, dass du denkst: Wow, aber sarkastisch. KVC sieht gar nicht so nützlich aus. In der Tat sieht es "wortreich" aus. Wenn Sie jedoch zur Laufzeit Änderungen vornehmen möchten, können Sie viele coole Dinge tun, die in anderen Sprachen viel schwieriger sind (dies geht jedoch über den Rahmen Ihrer Frage hinaus).

Wenn Sie mehr über KVC erfahren möchten, gibt es viele Tutorials, wenn Sie Google speziell in Scott Stevensons Blog verwenden . Sie können auch die NSKeyValueCoding-Protokollreferenz überprüfen .

Ich hoffe, das hilft.

Corey Floyd
quelle
12
valueForKey verhält sich für NSDictionary-Objekte unterschiedlich, je nachdem, ob der Schlüssel mit einem @ -Symbol beginnt.
Dreamlax
61
objectForKey: Akzeptiert jedes Objekt als Schlüssel, nicht nur Zeichenfolgen. Die einzige Voraussetzung ist, dass der Schlüssel das NSCopying-Protokoll unterstützt.
Ashley Clark
5
Ich bin überrascht, dass niemand diese Antwort korrigiert hat, indem er auf valueForKey hinweist: Technisch gesehen erhalten Sie keinen Zugriff auf die entsprechende Instanzvariable, sondern auf die Zugriffsmethode, mit der die Instanzvariable verwaltet werden kann (könnte).
Dany Joumaa
7
Warnung: valueForKey kann sehr langsam sein - es ist derzeit ein großer Engpass in meiner iPad-App, so langsam, dass das Ersetzen durch ein "Standard" -Wörterbuch die App spürbar schneller machte. Mit KVC unter iOS stimmt etwas nicht, und ich werde es nie wieder verwenden - es ist den Leistungsabfall nicht wert und ich muss es sowieso auf lange Sicht neu schreiben. Hierbei wurden NSString-Schlüssel mit NSString-Werten auf CALayers verwendet. Instrumente zeigten, dass "CAObject_valueForKey" 25% der gesamten Laufzeit (!) War
Adam
2
@ Adam Das klingt beängstigend. Haben Sie es seit iOS7 erneut versucht? Wenn ja, haben sich die Dinge seitdem geändert?
Unheilig
64

Wenn Sie dies tun valueForKey:, müssen Sie ihm einen NSString geben, während objectForKey:jede NSObject-Unterklasse als Schlüssel verwendet werden kann. Dies liegt daran, dass bei der Schlüsselwertcodierung die Schlüssel immer Zeichenfolgen sind.

Tatsächlich heißt es in der Dokumentation, dass selbst wenn Sie valueForKey:einen NSString angeben, dieser objectForKey:trotzdem aufgerufen wird, es sei denn, die Zeichenfolge beginnt mit einem @. In diesem Fall wird ein [super valueForKey:]Aufruf aufgerufen, valueForUndefinedKey:der möglicherweise eine Ausnahme auslöst .

Dreamlax
quelle
Können Sie mir bitte den Link der Dokumentation geben, die Sie meinen? Vielen Dank.
Ali Amin
5
@ عليامين: Es ist genau hier
Dreamlax
20

Hier ist ein guter Grund, objectForKey:wo immer möglich zu verwenden , anstatt valueForKey:- valueForKey:mit einem unbekannten Schlüssel wird die Meldung NSUnknownKeyException"Diese Klasse ist nicht für die Schlüsselwertcodierung für den Schlüssel kompatibel" ausgegeben.

Nick Locking
quelle
5
Gut zu wissen, dass "valueForKey: mit einem unbekannten Schlüssel eine NSUnknownKeyException auslöst, die besagt, dass" diese Klasse für den Schlüssel nicht mit der Schlüsselwertcodierung kompatibel ist "
onmyway133
Dies ist bei NSDictionary einfach nicht der Fall, und Sie können dies gerne ausprobieren: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey gibt solche Ausnahmen für andere Klassen aus, die keinen bestimmten Schlüssel unterstützen. Für NSDictionary-Unterklassen erhalten Sie jedoch nur eine ruhige Null. Versuchen Sie dies:
Motti Shneor
13

Wie gesagt, der objectForKey:Datentyp ist, :(id)aKeywährend der valueForKey:Datentyp ist :(NSString *)key.

Zum Beispiel:

 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. 

Nimmt also valueForKey:nur einen Zeichenfolgenwert an und ist eine KVC-Methode, während objectForKey:jede Art von Objekt verwendet wird.

Auf den Wert in objectForKeywird von derselben Art von Objekt zugegriffen.

Harjot Singh
quelle
0

Ich werde versuchen, hier eine umfassende Antwort zu geben. Viele der Punkte erscheinen in anderen Antworten, aber ich fand jede Antwort unvollständig und einige falsch.

In erster Linie objectForKey:handelt es sich um eine NSDictionaryMethode, während valueForKey:für jede KVC-Beschwerdeklasse - einschließlich NSDictionary - eine KVC-Protokollmethode erforderlich ist.

Wie @dreamlax schrieb, gibt die Dokumentation außerdem Hinweise darauf, dass NSDictionarydie valueForKey:Methode mithilfe ihrer objectForKey:Implementierung implementiert wird. Mit anderen Worten - [NSDictionary valueForKey:]ruft an [NSDictionary objectForKey:].

Dies impliziert, dass valueForKey:dies niemals schneller sein kann als objectForKey:(mit demselben Eingabeschlüssel), obwohl gründliche Tests, die ich durchgeführt habe, einen Unterschied von etwa 5% bis 15% gegenüber Milliarden von Direktzugriffen auf ein riesiges NSDictionary bedeuten. In normalen Situationen ist der Unterschied vernachlässigbar.

Weiter: Das KVC-Protokoll funktioniert nur mit NSString *Schlüsseln und valueForKey:akzeptiert daher nur eine NSString *(oder Unterklasse) als Schlüssel, während NSDictionaryes mit anderen Arten von Objekten als Schlüssel arbeiten kann - so dass die "untere Ebene" objectForKey:jedes kopierbare (NSCopying-Protokoll-kompatible) Objekt akzeptiert als Schlüssel.

Schließlich weicht die NSDictionary'sImplementierung valueForKey:von dem in der KVC-Dokumentation definierten Standardverhalten ab und gibt KEINEN NSUnknownKeyExceptionfür einen Schlüssel aus, den es nicht finden kann - es sei denn, dies ist ein "spezieller" Schlüssel - der mit '@' beginnt - was normalerweise ein "bedeutet. Aggregation "Funktionstaste (zB @"@sum, @"@avg"). Stattdessen wird einfach eine Null zurückgegeben, wenn kein Schlüssel im NSDictionary gefunden wird - und verhält sich genauso wieobjectForKey:

Es folgt ein Testcode, um meine Notizen zu demonstrieren und zu beweisen.

- (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
quelle