I want to create a NSDictionary with +[NSDictionary dictionaryWithObjectsAndKeys:]. One of my keys has a string but the string can sometimes be nil
. If the string is nil
, any other value key pairs I put afterward will be ignored because the list is prematurely terminated. What is the standard way to deal with the possibility that there might be a value with nil
in a NSDictionary
?
You need to check if the string is null. If it is, add [NSNull null]
instead of your string.
Creating NSDictionary objects can be combersome if you have many objects, that if they are nil, should not be included in the dictionary.
To me NSNull is as big a problem as it is the solution. When creating NSDictionary objects for usage in other objects, you can go two ways when dealing with nil values. Either you add NSNull objects to your dictionary - and then check for NSNull values when reading the values. This makes pretty code at the point of creation, but it becomes messy when reading out the values. Ex. should you check all keys in the dictionary, or are some garanteed to be not nil? And if an NSNull value is not filtered out it is bound to make exceptions when trying to send the object messages.
The second way is to just not add NSNull values to the dictionary. This is convinient when reading the NSDictionary, as the [someDictionary objectForKey:someKey] simply returns nil when the key is not set. This approach makes it pretty this easy when reading the values, but it really is bound to be messy code on creation. Ex. Creating a NSMutableDictionary, checking for nil before adding values, and finally returning an immutable copy?
Solution
To me the solution has been to create two categories, which in essense makes it easy for both the creating and the reading ends.
When creating the NSDictionary, you simply wrap you possible-nil values like this
[NSDictionary dictionaryWithObjectsAndKeysIngoringNull:
[NSNull nullWhenNil:val1], @"value1",
[NSNull nullWhenNil:val2], @"value2",
...
nil];
This code is almost as simple as approach number one, but it makes it substantially easier when reading out the values.
The categories is as follows (works in both ARC enabled and non-ARC code):
NSDictionary addition:
@implementation NSDictionary (NullAddition)
+ (id)dictionaryWithObjectsAndKeysIngnoringNull:(id)firstObject, ... {
NSMutableArray* objects = [NSMutableArray array];
NSMutableArray* keys = [NSMutableArray array];
va_list args;
va_start(args, firstObject);
for (id object = firstObject; object; object = va_arg(args, id)) {
id key = va_arg(args, id);
if (!key)
break;
if (![object isKindOfClass:[NSNull class]]) {
[objects addObject:object];
[keys addObject:key];
}
}
va_end(args);
return [self dictionaryWithObjects:objects forKeys:keys];
}
@end
NSNull addition
@implementation NSNull (NullAddition)
+ (id)nullWhenNil:(id)obj {
return (obj ? obj : [self null]);
}
@end
Good luck!
Attempting to insert data (key or value) into an NSDictionary
will result in a Run Time Error. Therefore, you do not have to deal with null data in the dictionary.
'-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: Test)'
and
'-[__NSCFDictionary setObject:forKey:]: attempt to insert nil key'
However, this means you are responsible for checking the validity of the data before putting it into the dictionary to prevent crashes.
I encounter a similar problem and google take me here. I need to add some kv into the dictionary whose value may be nil. Inspired by Trenskow's answer, I modify that function to support insert nil value, not terminate as before.
#define KV_END NSNull.null
NSDictionary *NSDictionaryOfKV(id firstKey, ...) {
__auto_type dict = [NSMutableDictionary dictionary];
va_list args;
va_start(args, firstKey);
for (id key = firstKey; key != NSNull.null; key = va_arg(args, id)) {
id obj = va_arg(args, id);
if (obj == NSNull.null) {
break;
}
if (key) {
dict[key] = obj;
}
}
va_end(args);
return dict.copy;
}
__auto_type value2 = nil;
__auto_type dict = NSDictionaryOfKV(key1, value1, key2, value2, key3, value3, KV_END);
You will got
{
key1: value1,
key3: value3
}
You can change the KV_END
to anything you want.
This make sense when your value is a variable, you can use it as-is, don't need to wrap it or like value ?: NSNull.null
.
© 2022 - 2024 — McMap. All rights reserved.