Reputation: 1472
I'm trying to use the writeToFile method of an NSArray
of NSDictionaries which doesn't allow nulls. I figured just loop through and find them and replace with empty NSString
's but I can't get it to work. This is what I'm trying.
EDIT: The NSArrays and NSDictionaries are created by NSJSONSerialization.
for (NSMutableDictionary *mainDict in mainArray)
{
for(NSMutableDictionary *subDict in mainDict)
{
for(NSString *key in subDict)
{
id value = [subDict objectForKey:key];
if (value == [NSNull null])
{
[subDict setObject:@"" forKey:key];
}
}
}
}
But this throws an exception. *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
But as you can see all NSDictionaries are mutable.
What's up? Is there a better way to do this?
EDIT: Moved new version to a separate answer.
Upvotes: 0
Views: 1154
Reputation: 2194
NSDictionary
does not allows any null values. Only [NSNull null]
object instances.NSDictionary
. Only NSMutableDictionary
entries. It seems your mainArray contains Immutable NSDictionary
instances.A graceful way to do what you want (BTW, are your sure you want empty strings in your file instead of removing keys whose values are [NSNull null]
?) is by creating a filtered and adjusted copy of your array and dictionaries -- and write that to file.
NSMutableArray *tempArray = [[NSMutableArray alloc] initWithCapacity:mainArray.count]
for (NSDictionary *mainDict in mainArray) { // scan the dictionaries in your mainArray
NSMutableDictionary *dict = [mainDict mutableCopy];
// replace NSNull objects with empty NSString objects
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL * _Nonnull stop) {
if (value == [NSNull null])
[dict setObject:@"" forKey:key];
}];
[tempArray addObject:dict];
}
[tempArray writeToFile:... ];
Upvotes: 0
Reputation: 1472
Or rebuilding the whole tree works.
NSMutableArray *newMainArray = [[NSMutableArray alloc] init];
for (NSArray *subArray in self.mainArray)
{
NSMutableArray *newSubArray = [[NSMutableArray alloc] init];
for(NSDictionary *dict in subArray)
{
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
for(NSString *key in dict)
{
id value = [dict objectForKey:key];
if (value == [NSNull null])
{
[newDict setObject:@"" forKey:key];
} else {
[newDict setObject:value forKey:key];
}
}
[newSubArray addObject:newdict];
[newDict release];
}
[newMainArray addObject:newSubArray];
[newSubArray release];
}
[self.mainArray release]; <---- ????
self.mainArray = newMainArray;
Upvotes: 0
Reputation: 104095
How do you know that the dictionaries are mutable? It looks like they’re not. Typing them as NSMutableDictionary
during the enumeration doesn’t make them mutable. You could walk over the dictionary and move the keys into a new, mutable one, filtering the nulls.
Also, if your dictionary comes from NSJSONSerialization
, take a look at NSJSONReadingOptions
, there’s an option to use mutable containers for the deserialized structure. That’s not always a good solution, but it’s worth checking out.
As for mutating a dictionary while enumerating it: you can’t change the keys, since that could easily throw off the enumeration code. But I don’t see why you couldn’t change the key values, which is all you’re after. This code works:
NSMutableDictionary *dict = [@{@"foo" : @"bar"} mutableCopy];
for (NSString *key in dict) {
dict[key] = @"baz";
}
Upvotes: 5