I have used NSMutable Dictionary and NSMutable Array. The datas are to be stored and retrieved from plist(Documents Directory) using NSArray of NSMutable Dictionary.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'
Please Guide me why its happened?.
Thanks!
It might help if you post the exact code that causes. My guess would be that while you are using NSMutableDictionary, the call to valueForKey: returns to you a non-mutable NSArray, and you think it is returning you an NSMutableArray instance. Note that mutable arrays and dictionaries allow you to manipulate the collection of items inside them, but do not guarantee you that those items themselves are mutable. For example, if you check the Property List Programming Guide: Reading and Writing Property-List Data, you will notice the following example:
If you load the property list with
this call:
NSMutableArray * ma = [NSMutableArray arrayWithContentsOfFile:xmlFile];
ma is a mutable array with immutable
dictionaries in each element. Each key
and each value in each dictionary are
immutable.
If you need explicit control over the mutability of the objects at each level, use propertyListFromData:mutabilityOption:format:errorDescription:
You can also create an explicit NSMutableArray copy from the NSArray you got from the NSMutableDictionary.
you said you are retrieving the array as a NSArray and not casting it to a NSMutableArray before you attempt to remove an object for it. This causes an error since you can't remove an object from an NSArray
Suppose [something dictionaryValue] returns an immutable dictionary, and you want a mutable version of that dictionary. It is not enough to say:
NSMutableDictionary *d = [something dictionaryValue];
This merely tells the compiler that d is an NSMutableDictionary, but really it's the same immutable dictionary you got from [something dictionaryValue]. Instead, you need to create a new, mutable copy of the dictionary:
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:
[something dictionaryValue]];
Similarly, use [NSMutableArray arrayWithArray:...] for arrays.
Related
I'm trying to add the dictionaries I have in one array to another array with dictionaries so I can have one array with the dictionaries of both arrays.
I have tried this:
[arrayOne arrayByAddingObjectsFromArray:arrayTwo];
But I'm getting the following error:
-[__NSDictionaryM arrayByAddingObjectsFromArray:]: unrecognized selector sent to instance 0x75ba1a0
- (NSArray *)arrayByAddingObjectsFromArray:(NSArray *)otherArray;
This function returns NSArray not NSDictionary, I guess in your code arrayOne is NSDictionary not NSArray right?
In Xcode source check warning shown for below line
[arrayOne arrayByAddingObjectsFromArray:arrayTwo];
To copy dictionary refer this: Copying the contents of an NSMutableDictionary into another NSMutableDictionary?
CoreData one-to-many default to generate NSSet ,how to change NSSet to NSMutableArray?I try to change it manually,but get error:
_NSFaultingMutableSet filteredArrayUsingPredicate:]: unrecognized selector sent to instance 0x1ed35e40'
NSSet has the method allObjects, which returns an NSArray.
To get an NSMutableArray you could do this:
NSMutableArray *array = [NSMutableArray arrayWithArray:myCoreDataObject.mySet.allObjects];
Note: order is not guaranteed to be the same every time (sets aren't ordered). If order is important to you, consider an NSOrderedSet instead.
See also the docs on NSSet:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSSet_Class/Reference/Reference.html
PS:
The reason you're getting said error:
NSSet (or _NSFaultingMutableSet for that matter) doesn't have a method called filteredArrayUsingPredicate.
I'm getting the following error when removing from my NSMutableArray
-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x1cdced10
2011-07-13 00:33:14.333 MassText[1726:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x1cdced10'
However right before I remove, I print out the array and the index. Neither are nil and I have no reason to believe why this error would be happening. Any ideas?
I had this problem. Mine was that I accidentally used type casting like this.
NSMutablearray * myarray = [[NSMutableArray alloc] init];
myarray =(NSMutableArray*) [mydictionary allkeys];
This will work for some time.. but if you are in a tight and large loop this tend to fail.
I changed my code to
NSMutableArray * myarray= [[NSMutablearray alloc] initWithArray:[mydictionary allKeys]];
The object is an NSArray, not an NSMutableArray.
You are calling removeObjectAtIndex on a NSArray instance. We can see clearly by your crash log.
At this point, four smart people (not including myself) have pointed out that you're sending -removeObjectAtIndex: to an object that thinks it's an immutable array. This would be a good time to start wondering why the array is immutable when you previously thought it was mutable. If you post some code that shows how the array is created, someone here will probably be able to show you what's going wrong.
One way that you can end up with an immutable array when you thought you had a mutable one is to copy a mutable array. For example, you might have a property:
#property (copy) NSMutableArray *myArray;
Perhaps you then create a mutable array, add some objects, and assign it to your property:
NSMutableArray *tempArray = [NSMutableArray array];
[tempArray addObject:#"You say goodbye"];
[tempArray addObject:#"I say hello"];
self.myArray = tempArray;
Now, does tempArray point to a mutable array or an immutable array? I haven't tested recently, but I'm pretty sure that you get an immutable array. You definitely get an immutableArray if you say:
NSMutableArray *foo = [tempArray copy];
So, start looking for places in your code where your array pointer is reassigned. After all, if your pointer really did point to a mutable array, it'd be awfully hard to explain the exception that you're getting.
The error says that you are trying to call the removeObjectAtIndex selector on an NSArray, which won't respond to that selector.
Make sure the array is really an NSMutableArray, not an NSArray.
I had the same problem, and it was because of the use of the copy method. I made one on my own returning a NSMutableArray* and it worked.
the code below is working, but I want to make sure it's correct. I'm nervous about having an empty Array inside my dictionary that I create from the plist, since typically it seems that if you don't, say, initWithCapacity:1 then you often get memory errors once you start trying to add items.
At least, that's been my experience with NSMutableDictionary. However, this is the first time I'm trying to implement nested data objects, so perhaps the reason this code works is that the nested array is automatically initialized when it's imported as part of its parent dictionary?
Any and all comments appreciated. Thanks.
First, here's what the plist looks like that I'm using to create my dictionary:
Next, here's my code where I'm using the plist to create a dictionary, then adding an item to dataArray
// Create a pointer to a dictionary
NSMutableDictionary *dictionary;
// Read "SomeData.plist" from application bundle
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"SomeData.plist"];
dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:finalPath];
// Now let's see if we can successfully add an item to the end of this empty nested array. How 'bout the number 23
NSNumber *yetAnotherNumber = [NSNumber numberWithInt:23];
[[dictionary objectForKey:#"dataArray"] addObject:yetAnotherNumber];
// Dump the contents of the dictionary to the console
NSLog(#"%#", dictionary);
Okay, fine, simple, good. When I Log the dictionary contents it shows that "23" has been added as an array value to dataArray. So the code works. But again, I want to confirm that I'm not "getting lucky" here, with my code just happening to work even though I'm not properly initializing that nested array. If so, then I could run into unanticipated errors later on.
So to sum up, dataArray is an empty array inside the .plist, so do I need to initialize it somehow (using, for example initWithCapacity: or something else) before I can properly populate it, or is the way I'm coding here just fine?
Thanks again.
EDIT
Hey all. I've been doing continued research on this, in the interests of finding a satisfying answer. I think I may have stumbled upon something, via this link on deep copying. His previous posts on deep copying had presented some code to do essentially what I was looking for above: create a mutable copy of a dictionary or array, from a plist, that also has mutable sub-structures.
However, as mentioned in the link above, it looks like these methods were superfluous, due to the CFPropertyListCreateDeepCopy method, which can be invoked with a call such as
testData = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, [NSDictionary dictionaryWithContentsOfFile:path], kCFPropertyListMutableContainersAndLeaves);
So, my question is, can I properly use CFPropertyListCreateDeepCopy, in the way shown, to achieve what I've been asking about here? In other words, can I use this method to import my dictionary from a plist with fully mutable, nested data objects?
As I mentioned in one of the comments, I know I can create a nested, mutable dictionary manually, but for complex data that's just not practical, and it seems unlikely that built-in methods to import a mutable plist don't exist. So, based on the above, it looks like I've possibly found the solution, but I'm still too new to this to be able to say for sure. Please advise.
(Side note: I would simply test the code, but as we've established, the current SDK is buggy with regard to allow you to edit immutable nested dictionaries, contrary to the documented behavior. So as before, I'm not just interested in whether this works, but whether it's correct)
Thanks in advance.
init... methods should only be called once, immediately after a call to alloc or allocWithZone:. When framework code creates and returns an object or graph of objects, their init... methods have already been called, so sending another init... message would have undefined results. Don't do that.
Interestingly, in spite of what the documentation appears to say (and admittedly I probably missed a key sentence or paragraph somewhere), when you create an instance of a mutable collection by reading a plist, any nested collections are also mutable. I ran the following little experiment in a test harness just to be sure:
NSMutableDictionary *pets = [NSMutableDictionary dictionaryWithContentsOfFile:#"/tmp/Pets.plist"];
NSMutableArray *cats = [pets objectForKey:#"cats"];
[cats addObject:#"Foo"]; // EDIT: Added line I accidentally omitted earlier
NSLog(#"%#", cats);
So again, the nested collections created when you read in the plist are fully initialized, and mutable to boot, so you can simply use them, as you've been doing.
EDIT
However, after doing some further reading of the docs, I think the OP is right to feel uneasy about relying on what is apparently an undocumented feature of the current version of the SDK. For example, the Property List Programming Guide states:
If you load the property list with
this call:
NSMutableArray * ma = [NSMutableArray arrayWithContentsOfFile:xmlFile];
ma is a mutable array with immutable
dictionaries in each element. Each key
and each value in each dictionary are
immutable.
So, to be on the safe side, if you need a nested collection to be mutable, you should create it yourself. For example, I'd recommend rewriting the code in the example above as follows:
NSMutableDictionary *pets = [NSMutableDictionary dictionaryWithContentsOfFile:#"/tmp/Pets.plist"];
NSArray *cats = [pets objectForKey:#"cats"];
NSMutableArray *mutableCats = [cats mutableCopy];
[pets setObject:mutableCats forKey:cats];
[mutableCats release];
You can then safely make changes to the nested mutable collection:
[mutableCats addObject:#"Foo"];
Any object in a dictionary which is created by reading from disk will be properly initialized. You will not have to do it on your own. However, as pointed out by jlehr, contents of the dictionary should be immutable. If you want the contents of the dictionary to be mutable, you will need to change them on your own. I have no idea why your program is not throwing an exception.
I do not know why you are getting memory errors while not using initWithCapacity:1 in other situations. The following code is perfectly valid:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:#"object1"];
[array addObject:#"object2"];
NSLog(#"%#",array);
[array release];
If you don't specify a capacity, the array won't pre-allocate any memory, but it will allocate memory as required later.
Edit:
It is perfectly acceptable to use NSDictionary with CFPropertyListCreateDeepCopy. In Core Foundation, a CFPropertyList can be a CFDictionary, CFArray, CFNumber, CFString, or CFData. Since NSDictionary is toll-free bridged to CFDictionary, you can use it wherever a CFDictionary is asked for by casting, and vice-versa. Your code as is will give a warning, but you can suppress it by casting the dictionary and return values.
NSDictionary *testData = (NSDictionary*)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)[NSDictionary dictionaryWithContentsOfFile:path], kCFPropertyListMutableContainersAndLeaves);
So I have a property NSMutableArray *grades. At the only place where I set this property, I am doing this:
NSMutableArray *array = [[NSMutableArray alloc] init];
self.grades = array;
[array release];
[self.grades addObject:#"20"];
The last statement generates an exception: -[NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'.
What in the world am I missing?
It sounds like the property is set to copy, which means the synthesized accessor makes an immutable copy of the array
Make sure grades is a NSMutableArray.
Edit:
copy returns an immutable copy, so you can't make changes. From Apple's Objective-C docs:
Copy
If you use the copy declaration attribute, you specify that a value is copied during assignment. If you synthesize the corresponding accessor, the synthesized method uses the copy method. This is useful for attributes such as string objects where there is a possibility that the new value passed in a setter may be mutable (for example, an instance of NSMutableString) and you want to ensure that your object has its own private immutable copy. For example, if you declare a property as follows:
Although this works well for strings, it may present a problem if the attribute is a collection such as an array or a set. Typically you want such collections to be mutable, but the copy method returns an immutable version of the collection. In this situation, you have to provide your own implementation of the setter method, as illustrated in the following example.
Copying the entire collection on assignment is a heavy operation. Are you sure you don't want to retain the collection, or just assign it?
If you really want a mutable copy, then write your own setter as the docs suggest.
- (void)setGrades:(NSMutableArray *)array {
// make shallow/deep copy here, and assign to `grades`, not `self.grades`
}
What is grades declared as?
From the looks of the error message your declaring grades as an NSArray and while this is valid it does mean that you lose the mutability of the array.
To maintain the array as mutable you'll need to declare grades as an NSMutableArray as well.
edit:
In light of your edit the reason could be that your using the copy keyword in the property, this would mean that when your assigning the array using self.grades the synthesised setter method makes an immutable copy of array
self.grades probably returns an NSArray if declared as #property NSArray* grades seeing this the compiler freaks and does not want to support addObject: method. You have 2 options
cast it
like [(NSMutableArray*)self.grades
addObject:].
add the object before assigning the
array.