Différence entre objectForKey et valueForKey?

345

Quelle est la difference entre objectForKeyet valueForKey? J'ai regardé les deux dans la documentation et ils m'ont semblé les mêmes.

Dévoué
la source

Réponses:

403

objectForKey:est une NSDictionaryméthode. An NSDictionaryest une classe de collection similaire à an NSArray, sauf qu'au lieu d'utiliser des index, il utilise des clés pour différencier les éléments. Une clé est une chaîne arbitraire que vous fournissez. Deux objets ne peuvent pas avoir la même clé (tout comme aucun objet dans un NSArrayne peut avoir le même index).

valueForKey:est une méthode KVC. Cela fonctionne avec N'IMPORTE QUELLE classe. valueForKey:vous permet d'accéder à une propriété en utilisant une chaîne pour son nom. Ainsi, par exemple, si j'ai une Accountclasse avec une propriété accountNumber, je peux faire ce qui suit:

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

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

En utilisant KVC, je peux accéder dynamiquement à la propriété:

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

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

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

Ce sont des ensembles d'instructions équivalents.

Je sais que tu penses: wow, mais sarcastique. KVC ne semble pas très utile. En fait, ça a l'air "verbeux". Mais lorsque vous voulez changer des choses au moment de l'exécution, vous pouvez faire beaucoup de choses sympas qui sont beaucoup plus difficiles dans d'autres langues (mais cela dépasse la portée de votre question).

Si vous voulez en savoir plus sur KVC, il existe de nombreux tutoriels si vous Google en particulier sur le blog de Scott Stevenson . Vous pouvez également consulter la référence du protocole NSKeyValueCoding .

J'espère que cela pourra aider.

Corey Floyd
la source
12
valueForKey se comporte différemment pour les objets NSDictionary selon que la clé commence par un symbole @.
dreamlax
61
objectForKey: accepte n'importe quel objet comme clé, pas seulement des chaînes. La seule exigence est que la clé prenne en charge le protocole NSCopying.
Ashley Clark
5
Je suis surpris que personne n'ait corrigé cette réponse en soulignant valueForKey: techniquement ne vous donne pas accès à la variable d'instance correspondante, mais plutôt à la méthode d'accesseur qui (pourrait) gérer la variable d'instance.
Dany Joumaa
7
Avertissement: valueForKey peut être extrêmement lent - c'est actuellement un goulot d'étranglement majeur dans mon application iPad, si lent que le remplacer par un dictionnaire "standard" a rendu l'application sensiblement plus rapide. Quelque chose de très mal avec KVC sur iOS, et je ne l'utiliserai plus jamais - ne vaut pas la baisse des performances, et avoir à le réécrire de toute façon. Cela utilisait des clés NSString avec des valeurs NSString sur CALayers. Les instruments ont montré que "CAObject_valueForKey" représentait 25% du temps d'exécution total (!)
Adam
2
@Adam Cela semble effrayant. L'avez-vous réessayé depuis iOS7? Si oui, les choses ont-elles changé depuis?
Unheilig
64

Lorsque vous le faites, valueForKey:vous devez lui donner une NSString, alors que vous objectForKey:pouvez prendre n'importe quelle sous-classe NSObject comme clé. En effet, pour le codage valeur-clé, les clés sont toujours des chaînes.

En fait, la documentation indique que même lorsque vous donnez valueForKey:une NSString, elle sera invoquée de objectForKey:toute façon à moins que la chaîne ne commence par un @, auquel cas elle invoque [super valueForKey:], qui peut appeler valueForUndefinedKey:ce qui peut déclencher une exception.

dreamlax
la source
pouvez-vous me donner le lien de la documentation que vous voulez dire. Merci.
Ali Amin
5
@ عليامين: C'est ici
dreamlax
20

Voici une excellente raison d'utiliser objectForKey:autant que possible au lieu de valueForKey:- valueForKey:avec une clé inconnue, on NSUnknownKeyExceptiondira "cette classe n'est pas conforme au codage de la valeur de clé pour la clé".

Nick Locking
la source
5
bon à savoir que "valueForKey: avec une clé inconnue lancera NSUnknownKeyException en disant" cette classe n'est pas conforme au codage de la valeur de clé pour la clé "
onmyway133
Ce n'est tout simplement pas vrai avec NSDictionary, et vous pouvez essayer ceci: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey émettra de telles exceptions sur d'autres classes qui ne prennent pas en charge une clé spécifiée - mais pour les sous-classes NSDictionary - vous recevrez juste un nil silencieux. essayez ceci:
Motti Shneor
13

Comme dit, le objectForKey:type de données est :(id)aKeyalors que le valueForKey:type de données l'est :(NSString *)key.

Par exemple:

 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. 

Ainsi, valueForKey:ne prendra qu'une valeur de chaîne et est une méthode KVC, tandis que objectForKey:prendra tout type d'objet.

La valeur de objectForKeysera accessible par le même type d'objet.

Harjot Singh
la source
0

Je vais essayer de fournir une réponse complète ici. La plupart des points apparaissent dans d'autres réponses, mais j'ai trouvé chaque réponse incomplète et certaines incorrectes.

Tout d'abord, objectForKey:est une NSDictionaryméthode, tandis valueForKey:qu'une méthode de protocole KVC est requise pour toute classe de plainte KVC - y compris NSDictionary.

En outre, comme l'a écrit @dreamlax, la documentation indique la NSDictionarymise en œuvre de sa valueForKey:méthode en utilisant son objectForKey:implémentation. En d'autres termes - [NSDictionary valueForKey:]appelle [NSDictionary objectForKey:].

Cela implique que valueForKey:cela ne peut jamais être plus rapide que objectForKey:(sur la même clé d'entrée), bien que des tests approfondis que j'ai effectués impliquent une différence d'environ 5% à 15%, sur des milliards d'accès aléatoire à un énorme NSDictionary. Dans des situations normales - la différence est négligeable.

Suivant: Le protocole KVC ne fonctionne qu'avec des NSString *clés, donc valueForKey:n'acceptera NSString *qu'une clé (ou sous-classe) comme clé, tout en NSDictionarypouvant fonctionner avec d'autres types d'objets comme clés - de sorte que le "niveau inférieur" objectForKey:accepte tout objet copiable (compatible avec le protocole NSCopying) comme clé.

Enfin, l' NSDictionary'simplémentation de valueForKey:s'écarte du comportement standard défini dans la documentation de KVC et n'émettra PAS NSUnknownKeyExceptionde clé qu'il ne trouve pas - sauf s'il s'agit d'une clé "spéciale" - qui commence par "@" - ce qui signifie généralement un " agrégation "touche de fonction (par exemple @"@sum, @"@avg"). Au lieu de cela, il retournera simplement un zéro lorsqu'aucune clé ne sera trouvée dans le NSDictionary - se comportant de la même manière queobjectForKey:

Voici un code de test pour démontrer et prouver mes notes.

- (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
la source