3

I am having trouble with RestKit when I had a JSON response with @ symbols in the keys. After some debugging it seems the issue is happening in __NSCFDictionary

So I tried the following simple code:

NSArray *keys = [NSArray arrayWithObjects:@"@key1", @"@key2", nil];
NSArray *objects = [NSArray arrayWithObjects:@"value1", @"value2", nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects
                                                   forKeys:keys];
for (id key in dictionary) {
    NSLog(@"key: %@, value: %@", key, [dictionary valueForKey:key]);
}

And I am getting the following error:

[<__NSDictionaryI 0x618000268ac0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key key1.

Can someone please explain why I am getting this error and if there is any workaround?

jscs
  • 63,694
  • 13
  • 151
  • 195

2 Answers2

12

You can't use @ in the keys in conjunction with valueForKey:. NSDictionary has some documented but perhaps unexpected behavior in that case: it strips the @ and invokes [super valueForKey:] with the new key. That looks for the key on the object, not in the dictionary's contents. No such key exists on instances of NSDictionary, so an exception is raised.

You should in general use objectForKey: to retrieve values from an NSDictionary.

Credit must go to Ken Thomases for his comments below, correcting earlier versions of this answer.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • 2
    Awww nuts, +1 to you because your answer ([which is detailed further in this question](http://stackoverflow.com/questions/17670240/nsdictionary-keys-with-symbol)) is more correct than mine. – Michael Dautermann Mar 02 '14 at 07:40
  • 2
    This is somewhat incorrect. As is [documented](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/index.html#//apple_ref/occ/instm/NSDictionary/valueForKey:) in the `NSDictionary` class reference, if you call `-valueForKey:` on a dictionary with a key starting with `@`, it strips the `@` and calls through to `super`. Effectively, it accesses the properties of the dictionary, rather than its key-value map. So, `[dict valueForKey:@"@allKeys"]` returns the same as `dict.allKeys`. – Ken Thomases Mar 29 '15 at 23:33
  • Thanks for the correction, @KenThomases; I've revised and also pointed this question at an earlier one that has a great description of the problem by vikingosegundo. – jscs Mar 30 '15 at 08:34
  • 1
    You're welcome, but your edit still doesn't get it right. `[dict valueForKey;@"@someKey"]` will not look for `@"someKey"` in the dictionary's keys and retrieve that value. It will only look at the dictionary's *inherent* properties — the things for which it provides getters. Basically, `-valueForKey:` in the non-dictionary case accesses the object's properties. `NSDictionary` overrides this behavior to instead look in the key-value mapping it maintains, via `-objectForKey:`. But it provides a bypass mechanism to get back to the non-dictionary behavior, using this `@` prefix. – Ken Thomases Mar 30 '15 at 15:33
  • Argh. Thanks again, @KenThomases. I would just delete at this point if the answer weren't accepted. I've re-edited. – jscs Mar 30 '15 at 18:31
  • No need to delete. First, the exact behavior of the `@` prefix with `-valueForKey:` was always a side issue. The thrust of the answer, to use `-objectForKey:` and not `-valueForKey:` because the latter treats `@`-prefixed keys specially, was correct. Second, that's what editing is for. ;) – Ken Thomases Mar 30 '15 at 18:34
  • Fair enough, @KenThomases; I'm a big proponent of editing. It's more that there's _already_ a good answer, by vikingosegundo (that got it right the first time! :) – jscs Mar 31 '15 at 08:10
0

Because you copied the code of someone who uses valueForKey: instead of objectForKey: without understanding what each is good for.

objectForKey: is what you should use. It takes the key that you pass in, and looks up the value for that key.

valueForKey: is a highly complicated method that looks at the text in the key, and does all kind of complicated things, for example a key of @"@sum" will add up some values. If that is what you want, fine. If you want to look up a value, use objectForKey. It does what you think it should do, and is usually several times faster than valueForKey: anyway.

As a rule of thumb: If I asked you whether you should use objectForKey: or valueForKey:, and you don't know what the difference is, use objectForKey:

gnasher729
  • 51,477
  • 5
  • 75
  • 98