NSDictionary stringForKey: and casting [NSNull null] for NSString *

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

However, the method -[NSDictionary stringForKey:] returns [NSNull null] if the key is not in the dictionary, and if I do not perform the cast, the compiler yells at me.

Am I missing something?

EDIT:

My mistake ... and I think I'm starting to see the problem ...

As everyone noted, there is no stringForKey: method in NSDictionary, and yes, I was thinking about custom defaults when I asked a question ... so I came back and looked what I was doing ... here it is:

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

If I do not perform 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 would prefer not to write 10 lines, where one will do ...

This is bad? Where did I encounter problems?

ALSO:

Dictionaries come from the JSON library ... and NULL are valid values ​​in this case, so it’s possible that it’s not that NSDictionary returns them if the key is missing, but rather that the key is essentially there, and the value is actually equals zero.

+6
objective-c iphone
source share
5 answers

NSDictionary does not return instances of NSNull unless you or some other source put them there. In addition, NSDictionary does not respond to -stringForKey: Perhaps you are thinking about NSUserDefaults ?

If you have an instance of NSDictionary , and all you want to do is get a string from it, you can 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 the backup string as fallback - the function will use this if no objects are associated with the key key .

+14
source share

as Jonathan's code shows, it is better to use the isKindOfClass: method. This will protect you even if you change the method of extracting the object, etc., and this is a more accurate check.

Also remember that [someValue isKindOfClass:[NSString class]] returns YES only when someValue not nil And it is a NSString , which means that it is also enough to test nil values.

+7
source share

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

In the particular case shown, where the only thing you are going to do with the object is a direct test for pointer equality, your approach will work, but it is a source of potential future errors. This kind of hacker throw to silence the compiler’s warnings makes one difficult to look at and should be avoided if there is a better way.

The real problem, of course, is the previous cast, where NSObject* from objectForKey: is objectForKey: directly to NSString* without any questions. Currently, you can get rid of it because you think that you are guaranteed to get only strings or NSNull , but what happens if the JSON library changes or simply cannot fulfill this contract? You would 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; .... 
+6
source share

You can use the extension of this category to wrap this logic up. this is on github

NSDictionary + Verified.h:

 // NSDictionary+Verified.h // // Created by alexruperez on 08/05/13. // Copyright (c) 2013 alexruperez. All rights reserved. // #import <Foundation/Foundation.h> @interface NSDictionary (Verified) - (id)verifiedObjectForKey:(id)aKey; @end 

NSDictionary + Verified.m:

 NSDictionary+Verified.m #import "NSDictionary+Verified.h" @implementation NSDictionary (Verified) - (id)verifiedObjectForKey:(id)aKey { if ([self objectForKey:aKey] && ![[self objectForKey:aKey] isKindOfClass:[NSNull class]]) return [self objectForKey:aKey]; return nil; } @end 
+3
source share

A good test for checking for NULL, especially when testing parsed results from network APIs (especially JSON), is the isEqual: method. I test both existence and usefulness using ...

 NSString *someValue = [response objectForKey:MyAPIKeySomeValue]; if (!!someValue && ![someValue isEqual:[NSNull null]]) { // optionally test for class type // use someValue } else { // error out for required values; skip for optional values } 

More generally, if you expect JSON in a specific format, and the API does not meet this requirement, you might think of a candidate for porting to @try/@catch . You can then handle the abnormal / illegal response format and eliminate hard crashes regarding invalid data formats.

If the format is poorly defined, you can let your parsing procedures fail more elegantly by building a meaningful NSError .

+1
source share

All Articles