'-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object' - objective-c

- (NSMutableDictionary *)updateTemplates:(NSMutableDictionary *)oldTemplates
forSpecType:(NSString *)specType {
// oldTemplates is an NSMutableDictionary pulled from a plist
// specType is used for flexible paths, to eliminate duplicate code
// Make a dict of the parameters object (about to be overwritten)
NSMutableDictionary *parameters = [oldTemplates valueForKeyPath:
[NSString stringWithFormat:#"root.%#.parameters", specType]];
// Dump the new data into the matching object
[oldTemplates setValue:[updateTemplates valueForKeyPath:
[NSString stringWithFormat:#"data.%#", specType]]
forKeyPath:[NSString stringWithFormat:#"root.%#", specType]];
// Put the parameters back, since they don't exist anymore
/* Instant crash, with the debugger claiming something is immutable
* But I just used the exact same method on the line above
* updateTemplates isn't immutable either; it's only when I try to mutate
oldTemplates after putting in updateTemplates -- and only the update
seems to be breaking things -- that I get the exception and crash
*/
[oldTemplates setValue:parameters forKeyPath:
[NSString stringWithFormat:#"root.%#.parameters", specType]];
return oldTemplates;
}
I could set up a loop to write one object of updateTemplates.specType at a time so only those parts get replaced and then I don't have to do anything with the parameters, but if it's immutable now, it will be when I try to write to it again. That won't do me any good.

If I remember correctly, dictionaries created from plists or NSUserDefaults are immutable by default. You'll have to create a mutable copy manually:
NSMutableDictionary *parameters = [[oldTemplates valueForKeyPath:
[NSString stringWithFormat:#"root.%#.parameters", specType]] mutableCopy];

mutableCopy makes a shallow mutable copy, not a deep mutable copy. If you have an NSDictionary containing key/value pairs where the values are NSDictionary instances, mutableCopy will return a mutable dictionary containing those NSDictionary immutable instances as values.
You either need to do a deep copy or use the plist serialization functionality to decode the plist with the mutable collections option enabled. Or you could compose a new collection derived from the old.

You can simply do:
NSMutableDictionary* oldTemplates = [NSMutableDictionary dictionaryWithDictionary:[oldTemplates valueForKeyPath:
[NSString stringWithFormat:#"root.%#.parameters", specType]]];
This will create a mutable copy from an existing NSDictionary

Related

Objective-C and ARC: Why value stored to during its initialization is never read and Incompatible pointer

when I run the analyzer tool I'm getting value stored to (jsonArr and myrrh) during its initialization is never read on lines and Incompatible pointer types assigning to 'NSMutableArray * from 'NsDictionarty *'
Look at the 1st two lines:
NSDictionary *jsonArr = [[NSDictionary alloc] init];
jsonArr = [json objectForKey:#"categories"];
The first line creates and assigns a new dictionary.
The second line then reassigns a new value to the same variable. This throws away the original value. Hence the warning that the value is never used.
Those two lines should simply be:
NSDictionary *jsonArr = [json objectForKey:#"categories"];
Actually, even better would be:
NSDictionary *jsonDict = json[#"categories"];
Don't name a dictionary variable with Arr. It's confusing. And use modern syntax.
The issues with myArr are the same. Though you have the additional problem of trying to assign an NSDictionary to an NSMutableArray. Those two are in no way compatible.
Perhaps jsonArr is properly named and its type should be NSArray instead of NSDictionary. Even then, you can't assign an NSArray to an NSMutableArray. You need to make a mutable copy:
NSArray *jsonArr = json[#"categories"];
NSMutableArray *myArr = [jsonArr mutableCopy];
In Obj-C variables are typed, meaning only values conforming to that variable's type may be assigned.
In your code (which is so hard to read) myArr is of type NSMutableArray * (a reference to an NSMutableArray) while jsonArr is of type NSDictionary* (a reference to an NSDictionary)
You cannot assign the value in jsonArr to myArr without a type conversion operation (cast).

Is a NSMutableDictionary in a NSDictionary still mutable?

The question is as simple as the title:
Is a NSMutableDictionary in a NSDictionary still mutable? Is the mdict mutable below?
NSMutableDictionary *mdict = [[NSMutableDictionary alloc] init];
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:mdict, #"key", nil];
And, is a NSDictionary in a NSMutableDictionary still immutable?
Further, what if it's array/set instead of dictionary?
Absolutely! Mutability of an object does not change when you place it into a container.
When you place a mutable dictionary into another collection, mutable or immutable, that collection adds a reference to the mutable dictionary object, but it does not change it in any other way. Same goes for placing immutable objects into collections: collections reference these objects without changing their nature.
This remains true while your object is in memory. If you serialize it and then deserialize it back, the process of deserialization may remove mutability. For example, if you save NSMutableDictionary into NSUserDefaults and then read it back, you would get back an immutable dictionary.
Yes. Objects generally don't know when they're placed into a collection, so they can't change their behavior based on that. NSDictionary does copy its keys (precisely so you can change the original object without affecting the dictionary), but it just stores a normal reference to the value.
As long as you access your variables like so
NSMutableDictionary * tempDict = [mdict objectForKey: #"Key"];
NSMutableDictionary * tempDict2 = [arrayVar objectAtIndex: index];
The temp variables retain all the functionality as before

Why NSMutableArray doesn't get sorted?

NSFileManager *fileManager= [[NSFileManager alloc]init];
NSDirectoryEnumerator *myEnumerator= [fileManager enumeratorAtPath:[[theFolder URLByDeletingLastPathComponent]path]];
int f,size=0;
NSMutableArray *dirList=[[NSMutableArray alloc]init];
NSString *fpath;
while (fpath=[myEnumerator nextObject])
{
[dirList addObject:fpath];
}
[dirList sortedArrayUsingSelector:#selector(localizedStandardCompare:)];
dirList contains filenames like "name_0012345.tif". Despite the sort the array doesn't contain the file in the order i would see in the finder sorting by name.
-sortedArrayUsingSelector: is actually an NSArray method that returns a new sorted array (which you promptly ignore). You mean to use -sortUsingSelector:, which is an NSMutableArray method that rearranges the existing array itself.
It's pretty common in Cocoa to have one method for returning a modified immutable version of an object, and another method for modifying the mutable object itself (-stringByAppendingString: and -appendString:, for example).
From NSArray Class Reference
sortedArrayUsingSelector:
Returns an array that lists the receiving array’s elements in ascending order, as determined by the comparison method specified by a given selector.

Is it bad practice to return a mutable object when the return value is an immutable object?

If I have this method:
+ (NSDictionary *)dictionaryFromQueryString:(NSString *)queryString
{
NSMutableDictionary *this = [[NSMutableDictionary alloc] init];
NSArray *fields = [queryString componentsSeparatedByString:#"&"];
for(NSString *field in fields)
{
NSArray *fieldParts = [field componentsSeparatedByString:#"="];
NSString *value = #"";
if(fieldParts.count > 1)
{
value = [[fieldParts subarrayWithRange:NSMakeRange(1, fieldParts.count - 1)] componentsJoinedByString:#"="];
}
[this setObject:unescape(value) forKey:unescape(fieldParts[0])];
}
return this;
}
Is it then bad practice that I return a NSMutableDictionary instead of a NSDictionary?
Should I convert it to a NSDictionary with return [this copy];?
It depends.
Sergio's answer is correct, save for one very important issue:
What happens when your object that contains the mutable dictionary mutates the dictionary after another object retrieves it? Unless that other object is written specifically to support the potential that the dictionary might mutate, the other object is now going to be in an inconsistent state.
Given that copy is fast for a dictionary as it is a shallow immutable copy, you are generally far better off always returning a copy than returning a reference to the mutable version. If you find that your code is pounding on the method that makes a copy, then cache an immutable copy in your object and vend that, invalidating it whenever the mutable backing store changes.
I don't think it is bad practice. The net effect of doing this is that the receiver of your NSDictionary will not try to modify the object (although the object is mutable). This is perfectly safe and it makes sense since your consumer method is kept more general (it can work both with mutable and non mutable objects).
Returning an immutable object this way isn't really bad practice, because NSMutableDictionary is a subclass of NSDictionary. This is polymorphism, so it's 'all good.'
But I would probably return an autoreleased copy like this anyway:
return [NSDictionary dictionaryWithDictionary:this];

Objective-C method returns NSDictionary instead of NSMutableDictionary. Why?

I am using the included method to return a pointer to a NSMutableDictionary, that is contained in an NSArray. However, the NSMutableArray (theOne) is being created as a non-mutuable NSDictionary. This is a problem because I want to modify the dictionary after retrieving it with this method.
- (NSMutableDictionary*)getMatFromBoutKey:(NSString*) boutKey
{
/*
* Returns the mat object with the provided boutKey.
* Returns nil if no mat has that boutKey.
*/
NSUInteger idx = [[event objectForKey:#"mats"] indexOfObjectPassingTest:
^ BOOL (NSMutableDictionary* obj, NSUInteger idx, BOOL *stop)
{
return [[obj objectForKey:#"boutKey"] isEqualToString:boutKey];
}];
if (idx == NSNotFound)
return nil;
else {
NSMutableDictionary* theOne = [[event objectForKey:#"mats"] objectAtIndex: idx];
return theOne;
}
}
Here's an image of the debugger stopped on a breakpoint right after the theOne is first referenced.
Why isn't theOne mutable? How can I return a pointer to the NSMutableDictionary so that I can modify it after I get the value returned to me?
Thanks!
I'm going to assume that you have a dictionary of arrays. Then that array contains a bunch of regular dictionaries. So when you pull it out of the array it is still a regular dictionary regardless of what you assign it to.
For instance, take the following code for example
NSDictionary *dict = [[NSDictionary alloc] init];
NSMutableDictionary *mutDict = dict;
mutDict will contain a regular dictionary because it has not properly been casted to a mutable dictionary.
either make sure when you create the array that is at [event objectForKey:#"mats"] that you put NSMutable dictionaries inside of it OR
use
NSMutableDictionary* theOne = [[[event objectForKey:#"mats"] objectAtIndex: idx] mutableCopy];
When taking the data out
In general, I think it's better practice to work with immutable objects when mutability isn't strictly necessary. Mutable objects use more memory, and of course have the potential of being accidentally changed. Maybe in the block are being changed to the enumerator(I'm not sure, but it is possible. For faster indexing). If you want to change a mutable object is via mutableCopy. or using a other method.
Is it inserted as mutable elsewhere in the code? If so it should return as mutable, if not you can send it the mutableCopy message to get a mutable copy (that has a reference count of 1, so be sure to release it when necessary).