How to release an object from an Array? - objective-c

I am currently working on an demo app so I was a little sloppy how to get things done, however I run the "Build and Analyze" to see how many leaks I get,... well and there are a lot.
Source of teh proble is that I have a NSMutableArray and I add some Objects to it :
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:[[MyObject alloc] initWithText:#"Option1"]];
// I have like 100 lines like that and 100 complains
Now, xcode complains about a potential leak.
Can someone give me some advice how to handle that ?
Thanks.

The problem is that you're allocating an instance of MyObject which you have a responsibility to release. When you pass it to the array, the array also retains the object, so now both you and the array have to release it. You can simply autorelease the object, and the array will keep it retained until you remove the object from the array or destroy the array itself.
[arr addObject:[[[MyObject alloc] initWithText:#"Option1"]] autorelease];

Replace
[arr addObject:[[MyObject alloc] initWithText:#"Option1"]];
with
[arr addObject:[[[MyObject alloc] initWithText:#"Option1"] autorelease]];
Most collections (arrays, dictionaries) own the objects added to them. And, since you’ve sent +alloc to MyObject, you also own the object that’s just been instantiated. As the memory management rules say, you are responsible for relinquishing ownership of objects you own. Sending -autorelease to the newly instantiated object will do that.

Related

Objective-C: autoreleased object as parameter for method

could the following code lead to problems?
- (void) method1 {
NSMutableArray *myArray = [[[NSMutableArray alloc] init] autorelease];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
A sometimes-appearing crash in my app looks like it IS a problem; but I would not understand that... shouldn't myArray stay alive at least until the end of method1?
Thanks a lot for your help!
So my questions besides "can that be a problem" are:
- would it be enough to remove autorelease and make a release at the end of the method?
- if not: do I have to make a retain/release in "someMethod"?
EDIT: but this can be a problem, am I right?
- (void) method1 {
NSMutableArray *myArray = [self getArray];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
- (NSMutableArray) method2 {
return [[[NSMutableArray alloc] init] autorelease];
}
I'm going to assume for a moment that you meant this to be NSMutableArray rather than NSArray. If you really mean NSArray here, then it is all impossible. You can't "fill the array" on an immutable array.
Yes, this should work. Your problem is likely elsewhere. A common mistake here would be over-releasing something you put into myArray.
Looks like you're missing a couple of open brackets on the line where you allocate your array. Should be [[[NSMutableArray alloc] init] autorelease];
Your code is completely correct. Do not listen to people telling you to remove the autorelease and manually release the array after the call to someMethod:.
In 99% of cases using an autorelease'd object has absolutely no negative performance impact on your application. The only time you want to worry about it is in loops.
The [[[Foo alloc] init] autorelease] pattern is just the same as using a built-in helper method like [NSString stringWithFormat:...]. They both return an autoreleased object, but you probably don't worry about performance with the latter. Again, only worry about autorelease in large loops, or when Instruments tells you you have a problem.
Using the [[[Foo alloc] init] autorelease] style also keeps all the memory management on a single line. If you get used to typing [[[ you won't forget the release. If you are copying and pasting code, or moving code around, you won't accidentally lose the corresponding release because it's all on the same line.
It's also easier to review code that uses the [[[Foo alloc] init] autorelease] style because you don't have to go around hunting for the release further down the method to make sure the memory management is correct.
So in terms of readability and safety and correctness, your code is absolutely fine and good. This applies to both your original snippet and the follow up you added below. Performance of autorelease only becomes an issue when you have large loops.
Your crashing issue must be caused by some other factor that is not evident in the code you posted.
I also suggest you read the Memory Management Programming Guideline if you have not already. Basically, and autorelease'd object is guaranteed to remain valid until the enclosing pool is released. In your case, the autorelease pool exists higher up in the call stack (probably the main runloop) so you can be safe in the knowledge that your autorelease'd array will remain valid for the duration of any calls you make.
One last point. Your array allocation code could also make use of the array helper constructor:
NSMutableArray *myArray = [NSMutableArray array];
This is even simpler, cleaner and shorter than your original.
Best thing to do will be something like that:
- (void) method1 {
NSMutableArray *myArray = [[NSMutableArray alloc] init];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
[myArray release];
}
and it's best from two different angles. One way you are holding your array till you have no need for it, so it will no wipe from memory until you say so. Second is the same sentence, only the reason is that you will clean up memory from no more needed objects as soon as it could be done.The last may need some more explanation... Autoreleased objects should be autoreleased as soon as you have no need for it, but they are released on two basic rules: at the end of block(not always) or at the memory pressure after end of the block(yes always). In other words NSAutoreleasePool doesn't always release stuff at the end of some block of the code, and it's not even close to be rare that it delays those releases to later point.Anyway you should always check for over-releasing your object as it will lead to crash when you'll try to reach such object at your code.

NSMutableArray memory leak

XCode is reporting a memory leak on a specific line of code:
(NSArray*)myFunction{
NSMutableArray * tempMapListings=[[NSMutableArray alloc] init]; //Xcode says leak is here
//do a bunch of stuff to insert objects into this mutable array
return tempMapListings;
[tempMapListings release]; // but I release it ?!
}
Is this due to releasing as an NSArray an mutable array? Since mutable inherits from inmutable, I wouldn't think this is a problem, and in any case, the object is released anyway. I'd appreciate the advice of a second eye.
No you're not releasing it. The return statement really ends the execution of the method at that point. So, the line below it, in your case
[tempMapListings release]; // but I release it ?!
is not executed.
Instead, you use autorelease:
-(NSArray*)myFunction{
NSMutableArray * tempMapListings=[[NSMutableArray alloc] init];
//do a bunch of stuff to insert objects into this mutable array
return [tempMapListings autorelease];
}
You can learn about autorelease in many places. Look for it in Apple's own documentation; you can also google it.
You're releasing tempMapListings after your return from the function. After a return statement, no more code is executed on that branch. Ergo, your [tempListListings release] statement is never run. Moreover, as you're returning it, you don't actually want to release it straight away - the caller will never have a chance to retain the array!
Autorelease pools are your friend here. Objects added to an autorelease pool are released on your behalf "eventually", giving your caller time to grab the result. To add your object to the default pool, change your allocation line to
NSMutableArray *tempMapListings = [[[NSMutableArray alloc] init] autorelease];
and remove that last release call.
For more information on autorelease pools, have a read of Apple's documentation. They're really quite useful.

Deallocating NSMutableArray of custom objects

I need help with deallocation of my NSMutableArray of custom objects. I need to retain the array and so I have added a property in .h and I release it in dealloc in .m file. When I add objects to the array, I do the following:
myarray = [[NSMutableArray alloc] init];
[myarray addObject:[[mycustomObject alloc]initWithObject:obj1]];
[myarray addObject:[[mycustomObject alloc]initWithObject:obj2]];
Now, I don't know how to release mycustomobject. If I do the following:
[myarray addObject:[[[mycustomObject alloc]initWithObject:obj1] autorelease]];
I run in to problems when I access the array later. Please advice.
I don't think you understand how memory management in Cocoa works. The array will retain the objects you add to it, and it will release them by itself when the array no longer needs them (such as when you release the array).
In other words, add the autoreleased object to the array, and don't worry about its retain count after that. If you want to remove it from the array simply remove it (using removeObjectAtIndex: or something similiar). If you think you want to release the object without removing it from the array then you are doing something wrong, since that may leave a dangling pointer in your array that will cause you to crash later.
You should really really go over the documentation again, particularly the section on Object Ownership and Disposal.
The proper way to do this is to let the array maintain ownership of the custom object:
NSMutableArray * array = [[NSMutabelArray alloc] init];
for (id obj in anArrayOfObjects) {
mycustomObject * customObj = [[mycustomObject alloc] initWithObject:obj];
[array addObject:customObj];
[customObj release];
}
If you're having difficulties accessing your array later, then you're doing something wrong with the memory management of the array.

pointer memory management misunderstanding w/ objective-c

I'm using the iPhone SDK 3.0, but I think this is a general misunderstanding of how things work w/ c & memory management.
I've overridden the viewWillAppear method like this
#implementation MyViewController
- (void)viewWillAppear:(BOOL)animated {
NSArray *items = [NSArray arrayWithOjbects:self.searchButton, self.trashCan, nil];
[self.bottomBar setItems:items animated:YES];
}
// other stuff...
#end
when I try to switch away from the view controller above and switch back everything works properly.
BUT, my inclination is to "release" the original pointer to "items" because I think a reference to the NSArray is now held by bottomBar.
But when I do this (see code below) and try to switch away from the UIViewController, I get a memory management error (-[CFArray count]: message sent to deallocated instance 0xd5f530).
- (void)viewWillAppear:(BOOL)animated {
NSArray *items = [NSArray arrayWithOjbects:self.searchButton, self.trashCan, nil];
[self.bottomBar setItems:items animated:YES];
[items release];
}
Do I need to not release items in this case? Or am I doing something wrong?
Obviously, the empirical evidence indicates that I shouldn't release "items", but it's not clear to me why this is the case.
Thanks for any info/"pointers"!
You do not need to release it because you never init'd it. [NSArray arrayWithObjects:...] returns an autoreleased object. You are not responsible to release it, because it has had the autorelease message sent to it when it returned from the method. You only have to release what you init! (If you had used [[NSArray alloc] initWithObjects:...] you would have had to.)
When you call arrayWithObjects: on NSArray:
NSArray *items = [NSArray arrayWithObjects:self.searchButton, self.trashCan, nil];
You are returned an autoreleased array. The array is returned to you autoreleased, because you do not call alloc, new, or a method containing copy on it. This signifies that you do not need to memory manage that object. (Take a look at the Memory Management Programming Guide for Cocoa for more information)
However, it is then retained when you call setItems on self.bottomBar, passing the array as an argument, bumping its retain count up to 1, but then you release it, returning its retain count back to zero, which causes it to be deallocated.
Since the array is retained by self.bottomBar, this implies that it is managing the memory of the array. When it is no longer needed, the array will be released, implying that the class no longer needs the array, which is the correct way to manage the memory.
For heavens sake guys, just point people to the Memory Management Rules. Don't paraphrase them. Don't say "returns an autoreleased object" (which is not necessarily true, and is irrelevent even when it is true). Just point them to the rules.
The rules are a sum total of 9 paragraphs! There is no need to paraphrase them, abrieviate them, or restate them. They are clear and concise and explicit.
Read the rules, follow the rules, and you will have no memory management problems.
Here's the short version:
+[NSArray arrayWithObjects:] returns an object that you do not own, so no, you should not release it.
On the other hand, if you had done:
NSArray *items = [[NSArray alloc] initWithObjects:self.searchButton, self.trashCan, nil];
this creates an object with a retain count of 1, so you would need to release it to prevent it from leaking.
Check out the Memory Management Programming Guide for Cocoa for more details.

Who is responsible for releasing objects in an array when copying?

In Objective-C, if array1 is copied onto array2 using mutableCopy, and suppose the code is done in main(), who is responsible for releasing the objects contained in the array? Is it main() or array2?
I think the previous answers have missed the point, or else the asker was pretty unclear. The actual question isn't talking about either array, but rather the array contents:
who is responsible for releasing the objects contained in the array? Is it main() or array2?
Both array1 and array2 are responsible for releasing the objects.
From the NSArray documentation:
"Arrays maintain strong references to their contents—in a managed memory environment, each object receives a retain message before its id is added to the array and a release message when it is removed from the array or when the array is deallocated."
To begin with, each of the objects are retained by the NSArray array1. When you create array2 via -mutableCopy, you get an NSMutableArray which points to the same objects, and retains each of them again. If you were to release array1 at this point, when its dealloc method were called it would release each of the objects it contains. However, array2 has retained them, so the objects won't be destroyed — only when their retain count reaches 0, which would happen if array2 were destroyed and nobody else has retained any of the objects (or when they are removed from array2).
Since collection classes (arrays, sets, dictionaries, etc.) handle retaining and releasing their contents, all you have to worry about is retaining or releasing the collection itself. Since you used -mutableCopy, remember that you have implicitly retained array2, so you should release it when you're done with it.
I reference this guide for Memory Management in Obj-C. He has a section on Arrays and Dictionaries, here's an excerpt:
Arrays, dictionaries etc. generally retain any objects added to them. (When dealing with 3rd party collection type objects, always check the documentation to see if they retain or not). This means that these collections will take ownership of the object, and you do not need to retain before adding.
The comments for the posting are also useful
The ownership responsibilities are not changed by storing objects in an array. Here's an example:
int main(int argc, char *argv[])
{
// ...
NSObject *obj1 = [[NSObject alloc] init]; // owned
NSObject *obj2 = [[NSObject alloc] init]; // owned
NSObject *obj3 = [[[NSObject alloc] init] autorelease]; // not owned
NSMutableArray *array1 = [NSMutableArray arrayWithObjects: obj1, obj2, obj3, nil]; // not owned
NSMutableArray *array2 = [array1 mutableCopy]; // owned
// ...
[array2 release];
[obj2 release];
[obj1 release];
// ...
}
This code directly allocates obj1 and obj2, so it owns them and must release them, but it autoreleases obj3, so it doesn't have to release that. In the same way, it doesn't own the result of arrayWithObjects:, so it doesn't release that, but it does own the result of mutableCopy, so it must release that. The objects being stored in an array is irrelevant—all you need to care about is ownership.
Both arrays keep strong references to their content, so obj1, obj2, and obj3 won't be deallocated as long as the arrays exist—but that's a detail of the NSArray contract, it doesn't affect how you manage the ownership of the objects or the arrays.
These are all details of Cocoa's memory management conventions, not arrays.
It wouldn't make sense for a mutable array to be tied to an immutable array. main() would be responsible for releasing array1.
In my experience however, releasing objects only causes applications to crash. ObjC is fairly good at automatically managing memory. My Cocoa apps don't seem to ever need more memory than they started with, even after running several hours.