How does NSDictionary handle NIL objects?

∥☆過路亽.° 提交于 2019-12-05 00:16:28


Consider the code below. In essence, we get 2 strings, then we add these values to the NSDictionary.

However, i hit a weird bug. When fbAccessTokenKey is 0x0 (or nil), then twitterToken would not be added as well.

NSString *fbAccessTokenKey=[[UserStockInfo sharedUserStockInfo] getFBAccessTokenKey];
NSString *twitterToken=[[UserStockInfo sharedUserStockInfo] getTwitterAccessTokenKey];

NSDictionary *params= [[NSDictionary alloc] initWithObjectsAndKeys:
                       fbAccessTokenKey, @"fb_access_token", 
                       twitterToken, @"twitter_access_token", 

Why is this happening, and what is a good way of resolving this?


nil is used as a 'sentinel' for marking the "end of arguments" list. If twitterToken was nil, the runtime would go through your arguments, and once it got to twitterToken, it would think that it was up to the end of your list of objects and keys. This is due to the way that C/Obj-C is implemented when it comes to list arguments.

The alternative safe way to do it is to use an NSMutableDictionary, and check to see if your values are non-nil, then add them to the mutable dictionary like this:

NSString *fbAccessTokenKey = [[UserStockInfo sharedUserStockInfo] getFBAccessTokenKey];
NSString *twitterToken = [[UserStockInfo sharedUserStockInfo] getTwitterAccessTokenKey];

NSMutableDictionary *params = [NSMutableDictionary dictionary];
if (fbAccessTokenKey) [params setObject:fbAccessTokenKey forKey:@"fb_access_token"];
if (twitterToken) [params setObject:twitterToken forKey:@"twitter_access_token"];

For more technical info, there's a good article on Cocoa with Love:


You can use the NSNull object. Documentation here:


Rather than initializing with initWithObjectAndKeys. Why not instantiate an NSMutableDictionary and then add the key value pairs (or not if the key is null)?

NSMutableDictionary * params = [[NSMutableDictionary alloc] init];

if (fbAccessTokenKey)
    [params setObject:fbAccessTokenKey forKey:@"fb_access_token];

// ... etc

You could cast it back to an NSDictionary later if you want to keep it immutable from that point.


Just a note in response to Josh's comment, I should clarify that of course the cast will not magically convert the params NSMutableDictionary to an NSDictionary. But if you are passing it to code which requires an NSDictionary, the cast will let you treat it as such.

Josh's comment includes this code:

NSMutableDictionary * md = [[NSMutableDictionary alloc] init]; 
NSDictionary * d = (NSDictionary *)md; 
[d setObject:@"Yes I am" forKey:@"Still mutable?"]; 
NSLog(@"%@", d); // Prints { "Still mutable?" = Yes I am; }

This will generate the following compiler warning (and for me, with warnings generating errors, a compile error):

file:blah.m: error: Semantic Issue: 'NSDictionary' may not respond to 'setObject:forKey:'