¿Diferencia entre objectForKey y valueForKey?

Respuestas:

403

objectForKey:Es un NSDictionarymétodo. An NSDictionaryes una clase de colección similar a una NSArray, excepto que en lugar de usar índices, usa claves para diferenciar entre elementos. Una clave es una cadena arbitraria que proporciona. No hay dos objetos que puedan tener la misma clave (de la misma manera que no hay dos objetos en un NSArraypuede tener el mismo índice).

valueForKey:Es un método KVC. Funciona con CUALQUIER clase. valueForKey:le permite acceder a una propiedad usando una cadena para su nombre. Entonces, por ejemplo, si tengo una Accountclase con una propiedad accountNumber, puedo hacer lo siguiente:

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

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

Usando KVC, puedo acceder a la propiedad dinámicamente:

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

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

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

Esos son conjuntos equivalentes de declaraciones.

Sé que estás pensando: wow, pero con sarcasmo. KVC no parece tan útil. De hecho, se ve "prolijo". Pero cuando desea cambiar las cosas en tiempo de ejecución, puede hacer muchas cosas interesantes que son mucho más difíciles en otros idiomas (pero esto está fuera del alcance de su pregunta).

Si quieres aprender más sobre KVC, hay muchos tutoriales si buscas en Google especialmente en el blog de Scott Stevenson . También puede consultar la Referencia del protocolo NSKeyValueCoding .

Espero que ayude.

Corey Floyd
fuente
12
valueForKey se comporta de manera diferente para los objetos NSDictionary dependiendo de si la clave comienza con un símbolo @.
dreamlax
61
objectForKey: acepta cualquier objeto como clave, no solo cadenas. El único requisito es que la clave sea compatible con el protocolo NSCopying.
Ashley Clark
55
Me sorprende que nadie haya corregido esta respuesta señalando valueForKey: técnicamente no le da acceso a la variable de instancia correspondiente, sino al método de acceso que (podría) administrar la variable de instancia.
Dany Joumaa
77
Advertencia: valueForKey puede ser intensamente lento: actualmente es un importante cuello de botella en mi aplicación de iPad, tan lento que reemplazarlo con un diccionario "estándar" hizo que la aplicación sea notablemente más rápida. Algo muy mal con KVC en iOS, y nunca lo volveré a usar, no vale la pena la caída en el rendimiento y tener que reescribirlo de todos modos. Esto estaba usando claves NSString con valores NSString en CALayers. Los instrumentos mostraron que "CAObject_valueForKey" era el 25% del tiempo de ejecución total (!)
Adam
2
@ Adam, eso suena aterrador. ¿Lo has intentado de nuevo desde iOS7? Si es así, ¿han cambiado las cosas desde entonces?
Unheilig
64

Cuando lo haga valueForKey:, debe darle un NSString, mientras que objectForKey:puede tomar cualquier subclase de NSObject como clave. Esto se debe a que para la codificación clave-valor, las claves siempre son cadenas.

De hecho, la documentación indica que, incluso cuando le da valueForKey:un NSString, invocará de objectForKey:todos modos a menos que la cadena comience con un @, en cuyo caso invoca [super valueForKey:], lo que puede llamar, lo valueForUndefinedKey:que puede generar una excepción.

dreamlax
fuente
¿Me puede dar el enlace de la documentación que quiere decir? gracias.
Ali Amin
55
@ عليامين: Está aquí
dreamlax
20

Aquí hay una gran razón para usar objectForKey:siempre que sea posible en lugar de hacerlo valueForKey:: valueForKey:con una clave desconocida aparecerá NSUnknownKeyException"esta clase no es compatible con la codificación del valor de clave para la clave".

Nick Locking
fuente
55
es bueno saber que "valueForKey: con una clave desconocida arrojará NSUnknownKeyException diciendo" esta clase no es compatible con la codificación del valor de clave para la clave "
onmyway133
Esto simplemente no es cierto con NSDictionary, y puede probar esto: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey emitirá tales excepciones en otras clases que no admitan una clave específica, pero para las subclases NSDictionary, solo recibirá un silencio nulo. intente esto:
Motti Shneor
13

Como se dijo, el objectForKey:tipo de datos es :(id)aKeymientras que el valueForKey:tipo de datos es :(NSString *)key.

Por ejemplo:

 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. 

Por lo tanto, valueForKey:tomará solo un valor de cadena y es un método KVC, mientras objectForKey:que tomará cualquier tipo de objeto.

Se objectForKeyaccederá al valor en el mismo tipo de objeto.

Harjot Singh
fuente
0

Trataré de proporcionar una respuesta integral aquí. Gran parte de los puntos aparecen en otras respuestas, pero encontré cada respuesta incompleta y algunas incorrectas.

En primer lugar, objectForKey:es un NSDictionarymétodo, mientras que valueForKey:es un método de protocolo KVC requerido de cualquier clase de queja KVC, incluido NSDictionary.

Además, como escribió @dreamlax, la documentación sugiere que NSDictionaryimplementa su valueForKey:método USANDO su objectForKey:implementación. En otras palabras, [NSDictionary valueForKey:]llama [NSDictionary objectForKey:].

Esto implica que valueForKey:eso nunca puede ser más rápido que objectForKey:(en la misma tecla de entrada) aunque las pruebas exhaustivas que he realizado implican una diferencia de aproximadamente 5% a 15%, sobre miles de millones de acceso aleatorio a un enorme NSDictionary. En situaciones normales, la diferencia es insignificante.

A continuación: el protocolo KVC solo funciona con NSString *claves, por valueForKey:lo tanto , solo aceptará una NSString *(o subclase) como clave, mientras que NSDictionarypuede trabajar con otros tipos de objetos como claves, de modo que el "nivel inferior" objectForKey:acepte cualquier objeto copiable (compatible con el protocolo NSCopying) como clave

Por último, la NSDictionary'simplementación de se valueForKey:desvía del comportamiento estándar definido en la documentación de KVC y NO emitirá una NSUnknownKeyExceptionclave que no puede encontrar, a menos que sea una clave "especial", una que comienza con '@', que generalmente significa " agregación "tecla de función (por ejemplo @"@sum, @"@avg"). En cambio, simplemente devolverá un valor nulo cuando no se encuentre una clave en NSDictionary, comportándose igual queobjectForKey:

Lo siguiente es un código de prueba para demostrar y probar mis notas.

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