PDA

View Full Version : NSMutableArray not mutable?!




Mac Me Up
Aug 26, 2008, 07:00 AM
Ok, so I'm relatively new to Objective-C, but not to programming. I have set up an NSMutableArray to hold a collection of objects, that I want to add and remove from. So in my interface:
NSMutableArray *savedLocations;

if I try to add or remove objects from this array I get an exception:
"mutating method sent to immutable object"

I can get around this by copying the array adding my object, and then assigning the pointer to the new object, but this just seems silly.

NSMutableArray *test = [savedLocations mutableCopy];
[test addObject:location];
self.savedLocations = test;
[test release];


I figure I must be doing something rather stupid. Can someone give me any pointers about this?



robbieduncan
Aug 26, 2008, 07:09 AM
Please post the code that actually matters: where does something get assigned to savedLocations? Most likely you are really ending up with a NSArray, not an NSMutableArray...

Mac Me Up
Aug 26, 2008, 07:26 AM
The code is simply:

[savedLocations addObject:myLocation];

robbieduncan
Aug 26, 2008, 07:31 AM
No, that is where you try and use the array. I am asking you where you either create an array object or get an array object from elsewhere. Sounds like you are doing this:


NSMutableArray *savedLocations;
[savedLocations addObject:someObject];


This will never work: you have not created an array. You have reserved space for a pointer to an array, but it's not pointing at one.

You need to either use an alloc/init pair, a convenience constructor or otherwise get an array.

If this is what you are doing it represents a fundamental lack of understanding of what you are doing and you should stop writing code and go back to the beginning and read the language documentation...

Mac Me Up
Aug 26, 2008, 08:05 AM
Ok...I think maybe I'm not being specific enough. I am doing this in my init method:
savedLocations = [[NSMutableArray alloc] init];

then I am trying to do this in another method:

-(void)saveLocation:(SavedLocation *)location{
[savedLocations addObject:[location retain]];
}


When I call that method I get an error. If I restructure that method to look like this:

-(void)saveLocation:(SavedLocation *)location{
NSMutableArray *temp = [savedLocations mutableCopy];
[temp addObject:location];

self.savedLocations = temp;
[temp release];
}

The error goes away.

This class I am doing all this in is a Singleton, but I wouldn't think that this would make any difference?

robbieduncan
Aug 26, 2008, 08:20 AM
Hmm, your initialisation is correct. As the error you are getting is a run-time not compile-time error (right) you must be assigning a non-mutable NSArray to that variable at some point. You'll need to go over your code till you find it.

I would also note that

[savedLocations addObject:[location retain]];

will leak memory: you don't need the retain as addObject will retain whatever you pass to it.

Mac Me Up
Aug 26, 2008, 08:31 AM
Thanks for your help. I commented out all my code for that class and started again. Now it works fine. Thanks for the tip on the retain as well, I had been wondering about that actually. As a JAVA dev all this release retain stuff is tedious, but the rules do seem fairly simply (release what you alloc, etc) I just wasn't quite sure how it works with arrays. Might have to do some more reading I think :D

robbieduncan
Aug 26, 2008, 08:37 AM
but the rules do seem fairly simply (release what you alloc, etc) I just wasn't quite sure how it works with arrays. Might have to do some more reading I think :D

The rules are simply. You already have most of them :D!

If you create an object via alloc/init or copy you must release it. An object you get via any other means (convenience constructors like [NSArray array] or returned from a method call etc) should be assumed to be autoreleased unless otherwise noted.

This also goes for objects you pass to other objects: if the other object wants to keep the passed object around it will retain it. So in practical terms anything you pass to NSMutableArray via addObject: or to a NSMutableArray via setObject:forKey: for example will be retained: you should pass autoreleased objects to them.

Mac Me Up
Aug 26, 2008, 10:26 PM
Ok so my last dumb questions, is there any cases where I need to deallocate NSStrings?

Eg:

NSString *temp = @"sdfsdfsdfds";



NSString *temp2 = [otherString stringByAppendingString:@"appendMe"];

etc?

My assumption is no, but maybe I'm wrong?

ayasin
Aug 26, 2008, 10:45 PM
Here's a rule of thumb that might help you. If the name of the selector you called starts with init you need to call release. If it doesn't then it's autoreleased for you. Conversely if the selector didn't start with init and you want to keep it around you need to call retain.

davedelong
Aug 26, 2008, 11:07 PM
Here's a rule of thumb that might help you. If the name of the selector you called starts with init you need to call release. If it doesn't then it's autoreleased for you. Conversely if the selector didn't start with init and you want to keep it around you need to call retain.

-copy and -mutableCopy are NOT autoreleased, nor is NSArray's -componentsJoinedByString, nor is a straight up +new. I've run up against all of these in the past 24 hours. :p

Edit: IIRC, strings declared literally using @"" are internally a subclass of NSString called NSConstantString and are cleaned up automatically for you, since they're constant. Interesting side note:
NSString * string1 = @"Hi";
NSString * string2 = @"Hi";
NSLog(@"%x %x", *string1, *string2);

The compiler will recognized that they're the same string and only create one instance of NSConstantString. Comparing the pointers will show that they're equivalent and identical. The NSLog above will print the same memory address for both string1 and string2, indicating that they both point to the same instance of NSConstantString.

Dave

kainjow
Aug 26, 2008, 11:14 PM
-copy and -mutableCopy are NOT autoreleased, nor is NSArray's -componentsJoinedByString

What are you basing that on?

davedelong
Aug 26, 2008, 11:16 PM
What are you basing that on?

I got a string back from it, tried releasing it, and crashed my app when it came time to pop my NSAutoreleasePool.

EDIT: WHOOPS, I meant to say it IS autoreleased when I was expect it to not be. LOL I feel dumb now. :p

ayasin
Aug 27, 2008, 05:25 PM
I got a string back from it, tried releasing it, and crashed my app when it came time to pop my NSAutoreleasePool.

EDIT: WHOOPS, I meant to say it IS autoreleased when I was expect it to not be. LOL I feel dumb now. :p

True, copy and mutableCopy not autoreleased, thanks for the correction (although a rule of thumb doesn't have to cover every case...just the general case ;)). On what basis did you expect componentsJoinedByString to not be autoreleased?