Diferença entre objectForKey e valueForKey?

345

Qual é a diferença entre objectForKeye valueForKey? Procurei os dois na documentação e eles pareciam o mesmo para mim.

Dedicado
fonte

Respostas:

403

objectForKey:é um NSDictionarymétodo An NSDictionaryé uma classe de coleção semelhante a uma NSArray, exceto que, em vez de usar índices, usa chaves para diferenciar itens. Uma chave é uma sequência arbitrária que você fornece. Dois objetos não podem ter a mesma chave (assim como dois objetos em um NSArraypodem ter o mesmo índice).

valueForKey:é um método KVC. Funciona com QUALQUER classe. valueForKey:permite acessar uma propriedade usando uma string para seu nome. Por exemplo, se eu tiver uma Accountclasse com uma propriedade accountNumber, posso fazer o seguinte:

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

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

Usando o KVC, eu posso acessar a propriedade dinamicamente:

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

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

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

Esses são conjuntos equivalentes de instruções.

Eu sei que você está pensando: uau, mas sarcasticamente. KVC não parece tão útil. De fato, parece "prolixo". Mas quando você deseja alterar as coisas em tempo de execução, pode fazer muitas coisas legais que são muito mais difíceis em outros idiomas (mas isso está além do escopo da sua pergunta).

Se você quiser saber mais sobre o KVC, existem muitos tutoriais se você pesquisar no Google, especialmente no blog de Scott Stevenson . Você também pode verificar a referência de protocolo NSKeyValueCoding .

Espero que ajude.

Corey Floyd
fonte
12
valueForKey se comporta de maneira diferente para objetos NSDictionary, dependendo se a chave começa com um símbolo @.
dreamlax
61
objectForKey: aceita qualquer objeto como uma chave, não apenas cadeias. O único requisito é que a chave suporte o protocolo NSCopying.
Ashley Clark
5
Estou surpreso que ninguém tenha corrigido esta resposta apontando valueForKey: tecnicamente não fornece acesso à variável de instância correspondente, mas ao método acessador que (pode) gerenciar a variável de instância.
Dany Joumaa 25/10/12
7
Aviso: o valueForKey pode ser intensamente lento - atualmente é um grande gargalo no meu aplicativo para iPad, tão lento que a substituição por um dicionário "padrão" tornou o aplicativo notavelmente mais rápido. Algo muito errado com o KVC no iOS, e nunca vou usá-lo novamente - não vale a pena a queda no desempenho e tenho que reescrevê-lo da maneira mais longa possível. Isso estava usando chaves NSString com valores NSString em CALayers. Os instrumentos mostraram que "CAObject_valueForKey" era 25% do tempo de execução total (!)
Adam
2
@ Adam Isso parece assustador. Você tentou novamente desde iOS7? Se sim, as coisas mudaram desde então?
Unheilig
64

Quando você valueForKey:precisa atribuir um NSString, ele objectForKey:pode usar qualquer subclasse NSObject como chave. Isso ocorre porque para a codificação de valor-chave, as chaves são sempre cadeias de caracteres.

De fato, a documentação afirma que, mesmo quando você fornece valueForKey:um NSString, ele será invocado de objectForKey:qualquer maneira, a menos que a cadeia inicie com um @, nesse caso, invoca [super valueForKey:], que pode chamar, o valueForUndefinedKey:que pode gerar uma exceção.

dreamlax
fonte
você pode me dar o link da documentação que você quer dizer? obrigado.
Ali Amin
5
@ عليامين: Está bem aqui
dreamlax
20

Aqui está um ótimo motivo para usar objectForKey:sempre que possível, em vez de valueForKey:- valueForKey:com uma chave desconhecida, NSUnknownKeyExceptiondizendo "esta classe não é compatível com a codificação do valor da chave".

Nick Locking
fonte
5
É bom saber que "valueForKey: com uma chave desconhecida lançará NSUnknownKeyException dizendo" esta classe não é compatível com a codificação do valor da chave "
onmyway133 15/14
Isso simplesmente não é verdade com o NSDictionary, e você pode tentar o seguinte: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey emitirá essas exceções em outras classes que não suportam uma chave especificada - mas para as subclasses NSDictionary - você receberá apenas um valor nulo. tente isso: #
Motti Shneor
13

Como dito, o objectForKey:tipo de dados é :(id)aKeyo valueForKey:tipo de dados :(NSString *)key.

Por exemplo:

 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. 

Portanto, valueForKey:levará apenas um valor de sequência e é um método KVC, enquanto objectForKey:levará qualquer tipo de objeto.

O valor em objectForKeyserá acessado pelo mesmo tipo de objeto.

Harjot Singh
fonte
0

Vou tentar fornecer uma resposta abrangente aqui. Muitos dos pontos aparecem em outras respostas, mas achei cada resposta incompleta e algumas incorretas.

Em primeiro lugar, objectForKey:é um NSDictionarymétodo, enquanto valueForKey:é um método de protocolo KVC exigido para qualquer classe de reclamação KVC - incluindo o NSDictionary.

Além disso, como o @dreamlax escreveu, a documentação sugere que NSDictionaryimplementa seu valueForKey:método USANDO sua objectForKey:implementação. Em outras palavras - [NSDictionary valueForKey:]chama [NSDictionary objectForKey:].

Isso implica que isso valueForKey:nunca pode ser mais rápido do que objectForKey:(na mesma chave de entrada), embora testes completos que eu tenha feito impliquem cerca de 5% a 15% de diferença, em bilhões de acesso aleatório a um enorme NSDictionary. Em situações normais - a diferença é insignificante.

Próximo: O protocolo KVC funciona apenas com NSString *chaves, portanto valueForKey:, aceita apenas uma NSString *(ou subclasse) como chave, enquanto NSDictionarypode trabalhar com outros tipos de objetos como chaves - para que o "nível mais baixo" objectForKey:aceite qualquer objeto com capacidade de cópia (compatível com o protocolo NSCopying) como chave.

Por fim, a NSDictionary'simplementação de valueForKey:desvios do comportamento padrão definido na documentação da KVC e NÃO emitirá um NSUnknownKeyExceptionpara uma chave que não pode encontrar - a menos que seja uma chave "especial" - que comece com '@' - que geralmente significa um " agregação "tecla de função (por exemplo @"@sum, @"@avg"). Em vez disso, ele simplesmente retornará um valor nulo quando uma chave não for encontrada no NSDictionary - comportando-se da mesma forma queobjectForKey:

A seguir, alguns códigos de teste para demonstrar e provar minhas anotações.

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