Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSDictionary stringForKey: and Casting [NSNull null] to NSString*

I was recently told casting [NSNull null] to (NSString*) was "terrible".

However, -[NSDictionary stringForKey:] method will return [NSNull null] if the key is not in the dictionary, and if I don't do the cast the compiler yells at me.

Am I missing something?

EDIT:

My mistake... and I think I might be beginning to see the problem...

As everyone has pointed out, there is no stringForKey: method on NSDictionary, and yes, I was thinking about the user defaults when I asked the question... so I went back and looked at what I was doing... here it is:

NSString * someValue = (NSString*)[myDictionary objectForKey:@"key"];
if (someValue == (NSString*)[NSNull null]) ...

if I don't do the cast in this case, I get the following error:

warning: comparison of distinct Objective-C types 'struct NSNull *' and 'struct NSString *' lacks a cast

Casting the value "solves" the problem, and I'd rather not write 10 lines where one will do...

Is this bad? Where will I run into problems?

ALSO:

The dictionaries are coming from a JSON library... and NULLs are valid values in this case, so perhaps it's not that NSDictionary returns them if the key is missing, but rather that the key is, in fact, there, and the value is actually null.

like image 425
Steve Avatar asked Jul 18 '10 01:07

Steve


3 Answers

NSDictionary does not return instances of NSNull unless you or some other source put them there. As well, NSDictionary does not respond to -stringForKey:. Are you perhaps thinking of NSUserDefaults?

If you have an instance of NSDictionary and all you want to do is get a string from it, you could try something like the following code.

NSString *MyGetString(NSDictionary *dict, id key, NSString *fallback) {
    id result = [dict objectForKey: key];

    if (!result)
        result = fallback;
    else if (![result isKindOfClass: [NSString class]])
        result = [result description];

    return result;
}

Pass a fallback string as fallback--the function will use that if no object is associated with the key key.

like image 67
Jonathan Grynspan Avatar answered Nov 08 '22 05:11

Jonathan Grynspan


as Jonathan's code shows, it's better if you use isKindOfClass: method. It'll protect you even if you change your method of retrieving the object, etc. and is a more accurate check.

Also remember that [someValue isKindOfClass:[NSString class]] returns YES only when someValue is not nil AND it's an NSString which means it's also enough for testing nil values.

like image 36
mohsenr Avatar answered Nov 08 '22 06:11

mohsenr


The cast is "bad" because NSNull is not an NSString. Casting does not change the actual type of the object pointed to, it only changes the compiler's, um, attitude to it.

In the very particular case shown, where the only thing you're going to do with the object is a direct test for pointer equality, your approach will work, but it's a source of potential future errors. This sort of hacky cast to silence compiler warnings makes one uneasy to look at and should be avoided if there's a better way.

The real problem, of course, is the previous cast, where the NSObject* from objectForKey: is cast straight to an NSString* with no questions asked. At present you can get away with it because you think you're guaranteed to get only strings or NSNull, but what happens if the JSON library changes or just fails to honour that contract? You'd really be better off doing something like:

NSObject* someValue = [myDictionary objectForKey:@"key"];
NSString* someStr = nil;
if (someValue == [NSNull null])
   ...
else if ([someValue isKindOfClass:[NSString class]])
   someStr = (NSString*) someValue;
....
like image 6
walkytalky Avatar answered Nov 08 '22 04:11

walkytalky