views:

53

answers:

2

This is a bit complex.

I've created an array (NSArray) called pointsOfInterest.

The elements of this array are NSDictionary objects. Each dictionary has two keys, "headerTitle" and "rowObjects." The value for "headerTitle" is a simple NSString; the object for "rowObjects" is another NSArray of objects; each of these is a custom class.

See the code below.

for (NSDictionary *dict in pointsOfInterest) {
    NSArray *keys = [dict allKeys];
    for (NSString *key in keys) {
        //NSLog(@"Key %@",key);
    }       
    NSString *category = [dict valueForKey:@"headerTitle"];
    [poiCategories addObject:category];
}

So, I'm enumerating through the array, getting individual dictionary objects into dict. I then run through all of its keys - if I uncommented the NSLog line, it would display something like this:

2010-08-06 14:00:17.236 TourGuide[4479:207] Key headerTitle
2010-08-06 14:00:17.237 TourGuide[4479:207] Key rowObjects
2010-08-06 14:00:17.238 TourGuide[4479:207] Key headerTitle
2010-08-06 14:00:17.239 TourGuide[4479:207] Key rowObjects

It then constructs a new, flat array, containing the values from all of the "headerTitle" keys.

First, I'm well aware that I should be able to do this:

poiCategories = [[pointsOfInterest valueForKey:@"headerTitle"] retain]

And get the values for the various "headerTitle" keys. Doing that, however, crashes the app. What's weird, is that the above for construct works fine... IF I leave in the internal for loop. If I remove that for loop, and its useless NSLog, leaving just this:

for (NSDictionary *dict in pointsOfInterest) {
    NSString *category = [dict valueForKey:@"headerTitle"];
    [poiCategories addObject:category];
}

Then it crashes. It does not grace me with any kind of trappable error that I can find.

Any reason anyone can think of?

+2  A: 

edit Louis pointed out in one of the comments that -[NSDictionary valueForKey:] will forward on to objectForKey: unless the key begins with @. Source However, I'd still change it to use objectForKey: just for semantic clarity.

Use objectForKey: instead of valueForKey:. They have very different semantics and implementations. valueForKey:@"headerTitle" will look for an NSDictionary method called headerTitle and return the result of that method (or if it doesn't exist, will likely crash), whereas objectForKey: actually goes and looks stuff up in the dictionary's hash table structure to find the object you're looking for.

Dave DeLong
+1. To be specific, `valueForKey:` is a Key-Value Coding method in an NSObject category. `objectForKey:` is the `NSDictionary`-specific method want want.
kperryua
Ok, noted - but why does the code work when that loop (the one containing the commented-out NSLog) is added?
Don Jones
valueForKey: and objectForKey: have different semantics, but in the case of NSDictionary valueForKey: does not do what you claim. It will actually call through to objectForKey: unless you string starts with an @, which means it will do what he thinks. It will only call through to the super implementation (which will attempt the method call) if you try to get the value for the key @"@headerTitle". See http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html#//apple_ref/occ/instm/NSDictionary/valueForKey:
Louis Gerbarg
@Louis thanks for correcting me! Editing answer.
Dave DeLong
No problem. I also agree, you should use objectForKey: in general, I only use valueForKey: against a dictionary when I am passing a dictionary into an interface that works against any KVC object (IOW, when the code doesn't know it is a dictionary anyway). I just wanted to point out that wasn't actually causing an error in this case.
Louis Gerbarg
+1  A: 

-[NSDictionary valueForKey:@"Foo"] actually calls -[NSDictionary objectForKey:@"Foo"] for you.

So either will work for NSDictionaries.

As for the crashes, when you run this in the debugger are you not seeing a stack trace? poiCategories is an allocated/inited NSMutableArray?

Firoze Lafeer