How to handle "mutating method sent to immutable object" exception? - iphone

I am having a class called Customer.
Customer *object; //in this object i have the following data varialbles.
object.customerName
object.customerAddress
object.customerContactList
I declared customerContactList as NSMutableArray and also allocated and initialized.
Now I am adding or deleting from contactList.
//Adding.
[object.customerContactList addObject:editcontacts];// here editcontacts one of the object in contactList.
//Deleting.
[object.customerContactList removeObjectAtIndex:indexPath.row];
[theTableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
I am getting exception even it is NSMutableArray. Please help me.
Thank You,
Madan Mohan

I'm jut guessing, because as Williham Totland said, we need to see your code. How is the customerContactList property declared, implemented and assigned?
My guess is that somewhere you are using copy on the array. If you send copy to an NSMutalbeArray you'll get an immutable copy back. If your property is declared:
#property (copy) NSArray* customerContactList
you'll get an immutable array back when you use it. You might even get an immutable array if you declare it thus (I'm not sure how clever the compiler is):
#property (copy) NSMutableArray* customerContactList

You keep asking this question, and you keep getting the same answer: "Izit?". Before you can be helped any further on this point, you need to actually show some more code, like the interface declaration for Customer.
What you should do, at this juncture, is to add a method to Customer along the lines of - (void)addToContactList:(id)contact. Accessing collection objects directly on an object in the manner you are describing is hardly considered kosher.
And another thing: your properties really don't need to have the class name in their name; rather than Customer *object; object.customerName; you should have Customer *customer; customer.name; and so on. Makes for far more readable code.
And this bears repeating: You cannot get that exception if you have an NSMutableArray. That exception is unequivocal proof that what you have, in fact, is not an NSMutableArray; and cannot be. This is the function of the exception in question. This is what the exception is, does, and means. This is the essence of the exception, it's reason for being, or raison d'etre, if you will. In case it wasn't clear: The array you are attempting to add to is not mutable. It is immutable.

Related

CoreDataGeneratedAccessor method giving "unrecognized selector sent to instance" error

When i call the method:
- (void)removeObjectFromMediaAtIndex:(NSUInteger)idx;
which is one of the default methods in a file created as a core data object, i'm getting an error of unrecognized selector sent to instance. Anybody know why this might be happening?
Ensure that your NSManagedObject sublcass instance was created using an NSManagedObjectContext and not directly. Instead of leveraging #synthesize for properties, NSManagedObject sublcasses leverage the #dynamic keyword which indicates the accessors will be created at runtime - in this case, by the NSManagedObjectContext. They will not be there if you create the object instance using something like alloc]init];
It is a notorious Core Data bug. It is almost 2-year old but sadly it is still there. See this post: Exception thrown in NSOrderedSet generated accessors.
It sounds like you may have altered your data model without altering the classes, or vice-versa. Or perhaps one of your team members did (my team quickly learned about this danger). Another possibility is that the reference you are using is not actually the class you think it is. Sometimes if you overrelease an object, another object will occupy the previous memory space but it will not be the correct class.
However, this doesn't look like a default method. The default methods I am used to seeing are add object, remove object, change to a new NSSet, and one more that I can't quite remember off the top of my head. However, if you got the CoreData object to use an NSArray instead it would make sense.

Modifying original data from detailView?

I'm working on iPhone and I'm using navigation.
I have list of data in RootViewController, and pass one data to detailViewController when a cell is clicked. Like this,
detailViewController.message = [tableData objectAtIndex:indexPath.row];
And I want to modify the content of 'message' in detailViewController. Is that possible?
I've tried that, but I get an error that it's immutable object. How can I do that? Somebody give me a hint. Thanks ;)
Added ------------------------------------------------
Ok. I'll specify the question.
in detailViewController, the message is decleared NSMutableDictionary* type.
and used like this.
NSMutableString *str = [m_message objectForKey:KEY_CONTENT];
[str appendFormat:#"appended!"];
And I've got this message.
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendFormat:'
Can't I just modify it's content like modifying data in c++ using pointer?
I think it would be best to use a delegate pattern for this. Create a DetailViewControllerDelegate protocol that informs its delegate of a changed message. Something along the lines of:
-(void)detailViewController:(DetailViewController *)controller didChangeMessage:(NSString *)message;
You could also directly manipulate the object by using a mutable one (NSMutableString, NSMutableArray etc.), but using a delegate improves reusability and decouples your classes.
EDIT:
The fact that your dictionary is mutable doesn't mean the object inside it are mutable as well. Your string str is probably immutable. Since NSMutableString is a subclass of NSString, the assignment will work though. You should make sure that you put a mutable string in the dictionary, or use the mutableCopy method of NSString to get a mutable copy of it.
It depends on what type of object you are passing. Immutable means that you cant alter the object. If you for exaple use NSArray, you have to switch to NSMutableArray instead. The same for string or dictionary.
Okay, i think i figured it out. The documentation for appendFormat says that appendFormat is for appending objects, just like when you use stringWithFormat.
The documentation states that:
The appended string is formed using
NSString's stringWithFormat: method
with the arguments listed.
You should use appendString to get the result you want.

I have to retain a NSMutableArray although it's a property

I'm new, but I read a lot about memory management, and really tried to find the answer myself.
It sounds so basic and yet I apparently don't get it.
I have a NSMutableArray as property in .h:
#property (nonatomic, retain) NSMutableArray *documentListArray;
I synthesize it and release it in (void) dealloc.
To populate the array, I have a method - (void)updateRecordList and in there:
self.documentListArray=[DocumentDatabase getDocumentListSortedByDate];
EDIT:next line:
[[self recordListTableView] reloadData];
where DocumentDatabase is a different class with the class methods getDocumentListSortedByDate and getDocumentList.
Here is what is happening in getDocumentListSortedByDate:
NSMutableArray *returnArray = [DocumentDatabase getDocumentList];
//sorting...
NSLog("array count:%i",[returnArray count]); //returns correct numbers first and second time
return returnArray;
and in getDocumentList
NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:files.count];
//add file data to array...
return returnArray;
This works the first time I call updateRecordList, but after adding a file and calling updateRecordList a second time, it crashes with (using NSZombies):
* -[NSCFNumber dealloc]: message sent to deallocated instance 0x7504c90.
With a lot of logging I narrowed the problem down to the line above in updateRecordList and it works if I change it to:
self.documentListArray=[[DocumentDatabase getDocumentListSortedByDate] retain];
My conclusion is that the array down in getDocumentList has been autoreleased before it arrives. So my questions are:
1. Why do I have to retain it there? Shouldn't that happen by itself by declaring the property (retain)?
Or, in other words, why is the array autoreleased too early (assuming this is what is happening)?
2. When I assign a new array to self.documentListArray, is the old array automatically released? If I try to release it myself before getting a new documentList, it crashes too.
Thanks in advance for any reply.
EDIT:
Maybe I'm an idiot: I failed to mention that documentListArray is the data source for an UITableView (see the added line on top). I suspect that I am doing something wrong with populating the table view, and the array gets retained...? It does however crash on assigning the property, not on reloadData.
I go back to study if I use the UITableViewDataSource protocol properly. Thanks to everybody, your answers brought me hopefully on the right track. Will update when solved.
EDIT2:
It works now without retaining, and I think I understand why: I debugged extensively and found that Objects contained in Objects added to the array where nil. Particularly, deep down in encodeWithCoder I did not use "self" when assigning values. When decoding, those values where nil. Since I changed that, it seems to work.
I suspect that not assigning the new array caused the crash, but the TableView which would read the new array-even before I call reloadData. Which would lead back to Davids question of synchroneous access. Thank you all for your help.
The code you've shown it appears correct; it should not be necessary (or correct) to call retain yourself, as long as you are assigning the value to a property with retain semantics before the autorelease pool is drained. Are all the calls (getDocumentListSortedByDate, getDocumentList) happening synchronously, or are you doing any of this in the background? Double-check that you're assigning using the "self." ("self.documentListArray =") instead of just assigning directly to the instance var ("documentListArray ="); if you omit the "self.", the setter is bypassed.
No, don't free the old value before assigning; that's the setter's job.

Assigning, mutable to immutable array?

Would someone be so kind as to confirm that I am understanding this correctly. My initial response to this was that the mutableArray when assigned to an immutableArray would become an immutableArray, however this is not the case.
Is this because the initial array is allocated as a mutableArray and the immutableArray is just assigned to point to the same object. The compiler gives a warning, but the the code executes just fine at runtime.
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:#"Teddy", #"Dog", nil];
NSArray *iArray = mArray;
[iArray addObject:#"Snoss"]; // Normally this would crash NSArray
much appreciated
gary.
Don't confuse the physical object that you just created, with how you are effectivley casting it in your code.
In this case, you did physically create a NSMutableArray.
Then you effectivley cast it to an NSArray - which is totally valid - for example, there are many cases where a function might want an NSArray, and you can pass it an NSArray, or anything derived from it (like an NSMutableArray).
The problem is that you get a compiler warning because you are trying to call addObject on an object which the compiler thinks is just an NSArray because that's what it's physical type is.
This will actually work, because it actually is an NSMutableArray, and in runtime would respond to that selector.
It's bad coding practice to do it this way, because NSArray doesn't actualy respond to the addObject selector. For example, I could create a function, like:
-(void) addIt:(NSArray)myThing {
[myThing addObject:#"New String"];
}
The compiler would give you a warning saying the "NSArray" doesn't respond to the "addObject" selector. If you cast an NSMutableArray, and passed it to this function, it myThing, it would actually work.
It's bad practice though, because if you actually passed an NSArray it would crash.
Summary: Don't confuse what the object really is vs. what you are making the compiler interpret it as.
Yes, you are right. You should do
NSArray *array = [NSArray arrayWithArray:mutableArray];
to ensure that nobody can change the array
Your iArray is actually referencing to NSMutableArray instance, that's why it is a NSMutableArray.
Obj-c doesn't have strict check on class types, all objects are of type 'id'.
You could write
NSNumber *iArray = mArray
Compiler will show a warning of wrong cast (not error). But it will work.
Don't mess with pointers, there is no object type transformations as you can expect in C++. (there are overloadable operators for casting object to another class).
Obj-c works with objects much like script/interpreted languages. Dynamic typing (objects only), reflection, dynamic change of methods of instance of classes etc - full flexibility. A perfect mix of speed of low-level C/C++ and flexibility of dynamism.
AFAIK, you are correct. NSMutableArray is a subclass of NSArray, so you can assign mArray to iArray here without a problem.
However, it isn't clean code and you should avoid it.

Strange problem with NSMutableArray - Possibly some memory corruption

I am trying to update data in a table view using a NSMutableArray. Quite simple :(
What is happening is that I get my data from a NSURLConnection Callback, which I parse and store it in an array and call reload data on the table view. The problem is that when cellForRowAtIndexPath is called back by the framework. The array still shows the correct count of the elements but all string objects I had stored earlier are shown as invalid.
Any pointers
Maybe your problem is something like the below
NSString *value = [NSString stringWithFormat:#"%#",stringFromWebService];
[arrayOfObjects addObject:value];
[value release]; // This should not be released
This may not be exact but could be similar. Here value is obtained from class method of NSString which is an autoreleased object so if you can see it will be released twice, one by autorelease and other by you.
This is the area you need to check.
Check that you are retaining the NSString objects in your NSURLConnection callback. Are you autoreleasing them?
Edit
Ok, forget that last thing. Double checking myself, NSMutableArray will automatically retain the objects when you add them to your array. So you won't need to retain them explicitly:
Like NSArray, instances of
NSMutableArray maintain strong
references to their contents. If you
do not use garbage collection, when
you add an object to an array, the
object receives a retain message. When
an object is removed from a mutable
array, it receives a release message.
So you need to check you aren't doing any other explicit releases on the objects you are adding to the array. Are they referenced anywhere else?
The problem is there where you are adding the string object to a mutable array. The string object was already invalid that time. That's why the time you are accessing them from the array, they are invalid or do not exist.
So best thing is to check the code where you are adding the string object during the parsing.
Your problem may be that you have not initiated the array correctly.
Are you using the array as an attribute of the class?
Make sure when you load the view you allocate and initiate the array.