Search code examples
debuggingnsstringnsdictionarynsurl

Objective-C Problems with NSDictionary and NSURL


I am new to Objective-C and I have no clue why this code is not working:

    NSMutableDictionary *bookmarks = [NSMutableDictionary dictionaryWithCapacity:(NSUInteger) 4];
[bookmarks setObject:@"Stanford University" forKey:[NSURL URLWithString:(NSString *) @"http://www.stanford.edu"]];
[bookmarks setObject:@"Apple" forKey:[NSURL URLWithString:(NSString *) @"http://www.apple.com"]];
[bookmarks setObject:@"Berkeley" forKey:[NSURL URLWithString:(NSString *) @"http://www.berkeley.edu"]];
[bookmarks setObject:@"CS193P" forKey:[NSURL URLWithString:(NSString *) @"http://cs193p.stanford.edu"]];

NSEnumerator *browser = [bookmarks keyEnumerator];
id each;
NSURL *url;
while ((each = [browser nextObject])) {
    url = [browser valueForKey:(NSString *)each];
    NSLog(@"%@", [url absoluteURL]);
}

The error I get is:

2009-06-29 11:25:22.844 WhatATool[2102:10b] *** -[NSURL length]: unrecognized selector sent to instance 0x1072c0
2009-06-29 11:25:22.845 WhatATool[2102:10b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL length]: unrecognized selector sent to instance 0x1072c0'

Any help would be appreciated. Thanks.


Solution

  • A key is what you use to access an object in the dictionary. You have your keys and objects reversed on every line you call -[bookmarks setObject:... forKey...] in the code. The problem in your case arises because you're trying to treat NSURL objects as NSString objects — [bookmarks objectForKey:(NSString *)each] — and the dictionary is trying to get the length of the supposed string by calling -length, which exists for NSString but not for NSURL.

    If you're always constructing a dictionary with the same objects, consider using a more compact "varargs" constructor for NSDictionary. (Note that the last comma-separated argument must be nil — see the related documentation) You can also use -[NSDictionary dictionaryWithObjects:forKeys:] with two NSArray objects, containing the URLs and the bookmark names. (Incidentally, casting string literals as NSString* to create an NSURL is completely unnecessary.)

    NSDictionary *bookmarks = [NSDictionary dictionaryWithObjectsAndKeys:
                               [NSURL URLWithString:@"http://www.stanford.edu"],
                               @"Stanford University",
                               [NSURL URLWithString:@"http://www.apple.com"],
                               @"Apple",
                               [NSURL URLWithString:@"http://www.berkeley.edu"],
                               @"Berkeley",
                               [NSURL URLWithString:@"http://cs193p.stanford.edu"],
                               @"CS193P",
                               nil];
    

    In addition, you have a lurking error that hasn't been mentioned yet: browser is an NSEnumerator, but you're calling valueForKey: as if it were an NSDictionary, rather than on bookmarks. That will cause a similar crash for an unrecognized selector, too. (Even for dictionary objects, you should call -objectForKey: instead; -valueForKey: is used mostly for/by Cocoa Bindings, and does extra work you don't need. I realize it's confusing since we think in terms of "key-value pairs", but there it is...)

    Finally, you can also simplify the enumeration code somewhat. (A for-in loop on an NSDictionary enumerates its keys just like -keyEnumerator would.)

    Here's how I'd suggest doing the last part:

    NSURL *url
    for (NSString *key in bookmarks) {
        url = [bookmarks objectForKey:key];
        NSLog(@"%@", [url absoluteURL]);
    }