Memory Leak - Objective c [duplicate] - objective-c

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Objective C Memory Management
My code is showing a memory leak here:
NSMutableArray* newImageArray = [[NSMutableArray alloc] init];
NSMutableArray* newMediaArray = [[NSMutableArray alloc] init];
if (self.categoryIndex == 0) {
for (int i=1; i < [categoryArray count]; i++)
{
newImageArray = [NSMutableArray arrayWithArray:[newImageArray arrayByAddingObjectsFromArray:[self getImageArrayByCategoryIndex:i]]];
}
}
else {
newImageArray = [self getImageArrayByCategoryIndex:self.categoryIndex];
}
for (int i=0; i < [newImageArray count]; i++)
{
Media* media = [[Media alloc] init];
NSString* imageFile = [newImageArray objectAtIndex: i];
media.imageFile = [UIImage imageNamed:imageFile];
media.imageLabel = [[imageFile lastPathComponent] stringByDeletingPathExtension];
media.soundFile = [appFolderPath stringByAppendingString:[[[imageFile stringByDeletingPathExtension] stringByAppendingString: #".wav"] stringByReplacingOccurrencesOfString: IMAGES_FOLDER withString: SOUNDS_FOLDER]];
[newMediaArray addObject:media];
}
self.mediaArray = newMediaArray;
[self setNextMediaIndex];
I am not releasing media because it is being used by newMediaArray (which is used by mediaArray, which is used my my main object). Shouldn't everything get released when I release my main object?

It looks like you are leaking all over the place in a variety of ways
newImageArray gets allocated but never released, additionaly you are overwriting the version that you allocated in the first line of you code with another version. So even if you released it at the end of this code segment, the wrong version would get released. It looks like you don't even need to allocate this one.
newMediaArray gets allocated but never released, you assign it to a property mediaArray if you are using #synthesize to create the code for that property, depending on how you declared that property, the setter will retain the value i.e. newMediaArray creating a leak.
media gets allocated but never released, it get added to a NSMutableArray which means it will get retained by the array. If your app crashes when you release media in the for loop the problem is somewhere else
The Memory Management Programming Guide is pretty much a must read

When an NSMutableArray such as newMediaArray adds an object, it will retain that object. You don't need to (nor should you) retain the object on the array's behalf. This is fundamentally how memory management in Objective-C works: each object retains the things it references, and releases them when finished. newMediaArray is its own object, so it'll manage its own references.
You should release media near the end of the body of your for loop because you're done using that object. If you don't release it then, you'll lose your reference to it and you'll have no way to release it in the future.

You do
[newMediaArray addObject:media];
that means that newMediaArray has done a retain on media. You can then release media (you should). The retain done in the array method will keep it alive as long as the array references it. If you don't release it in your method, the retain count will remain 2, and even if the array releases it, it will still be 1 and not be dealloc-ed.
You could do:
Media *media = [[[Media alloc] init] autorelease];
Then the autorelease pool will release it in time, but not before this method ends.

You need to have the [media release] statement at the bottom of the loop. newMediaArray should also be released after it is assigned to the mediArray property.

Related

Why is memory sometimes deallocated immediately and other times only when autorelease pool is drained?

I made simple experiment and found some strange behavior. Here some code - part of long method with ARC enabled:
MKObject *obj = [[MKObject alloc]init];
NSMutableArray *temp = [[NSMutableArray alloc]init];
[temp addObject:obj];
obj = nil;
temp = nil;
//here deallocating is called on obj (MKObject)
//other stuff
but if I change NSMutableArray to NSArray and literal initialisation
NSArray *temp = #[obj];
deallocating executed before autoreleasepool closed, not after setting nil to all references. Did I missed something ?
A few observations:
In your first example, neither the MKObject nor the NSMutableArray is an autorelease object, so these objects will be deallocated immediately, not waiting for the autorelease pool to drain:
MKObject *obj = [[MKObject alloc] init];
NSMutableArray *temp = [[NSMutableArray alloc] init];
[temp addObject:obj];
obj = nil;
temp = nil;
In your second example, the NSArray is an autorelease object, so the NSArray (and therefore, the MKObject) will not be deallocated until the autorelease pool is drained.
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
obj = nil;
temp = nil;
To understand why the array literal, #[], creates an autorelease object, one should note that it expands to +[NSArray arrayWithObjects:count:]. Autorelease objects are created whenever you instantiate an object with any method other than using alloc followed by an init (whether init, or one of the permutations, such as initWithObjects:).
As you observed, when an app creates autorelease object, the object will not be immediately be deallocated, but it will when the autorelease pool drains. Since we generally yield back to the runloop quickly (at which point the pool will be drained), the choice of autorelease objects or non-autorelease objects has little practical impact in simple cases. But if the app, for example, has a for loop in which it creates many autorelease objects without yielding back to the runloop, it could be problematic (especially if MKObject was large or you were doing this many times). For example:
for (NSInteger i = 0; i < 100; i++) {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
// Note, because they are local variables which are falling out of scope, I don't have to manually `nil` them.
}
Because we are instantiating autorelease NSArray objects in this example, the above would keep all 100 arrays and objects in memory until you yielded back to the runloop and the autorelease pool had a chance to drain. This means that the app's "high water mark" (the maximum amount memory it uses at any given time), would be higher than it might need to be. You could remedy this by either:
use a non-autorelease object (such as by using alloc/init) instead of using the array literal:
for (NSInteger i = 0; i < 100; i++) {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = [[NSArray alloc] initWithObjects:obj, nil];
}
or
by introducing your own, explicitly declared #autoreleasepool:
for (NSInteger i = 0; i < 100; i++) {
#autoreleasepool {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
}
}
In this final example, the autorelease pool will be drained for each iteration of the for loop, resolving any challenges with autorelease objects that would otherwise have their deallocation deferred until the end of the loop.
One final caveat: In general, methods that begin with alloc and init (or a variation of the init method), will not generate autorelease objects, whereas all other methods, such as arrayWithObjects:count: will generate autorelease objects. One notable exception is the NSString class, which due to internal memory optimizations, does not conform to this rule. So, if you have any doubt, you can employ your own manual #autoreleasepool if you are repeatedly instantiating and releasing objects, and you are unsure as to whether the objects are autorelease objects or not. And, as always, profiling your app with the Allocations tool in Instruments is a good way of observing the app's high water mark. For an illustration of how these various techniques can impact the memory usage of your app, see https://stackoverflow.com/a/19842107/1271826.
The array was being retained by the autorelease pool. As described in Clang's Objective-C Literals documentation, the array literal syntax expands to a call to +[NSArray arrayWithObjects:count:], which creates an autoreleased array.
A couple of things I see, though I'm not entirely clear on the question so I can't say which applies:
Adding an object to an NSArray or NSMutableArray increments the object's retain count.
In the first instance, you manually instantiate obj, which gives it retain count 1.
Adding it to the NSMutableArray makes its retain count 2.
In this case, obj = nil decrements retain to 1;
temp = nil tells the array to handle releasing its contents. Those w/retain count 0 get dealloc'd immediately.
In the 2nd instance with #[] literal creation, the literal syntax under the hood creates an autoreleased object using the method arrayWithObjects: count:. When this is no longer needed it goes to the autorelease pool for eventual deallocation.
It isn't an issue of the objects IN the array but the way the arrays themselves were created.
Edited my original response to address comments below - I was confusing the issue.

Is this a memory leak (using NSArray and NSCopying)

I tried something out in my code to see the effect on memory utilization. I wanted to find out if the line inside the loop was leaking. Running this loop took utilization up to 100MB and it didn't go back down again. Does this indicate a memory leak? If so why? (I'm using ARC)
for (i = 0; i < 10000000; i++)
{
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
(accounts is an array of AccountSummary objects which implements NSCopying like this: name city state phone are all NSStrings, isLocal is BOOL)
- (id)copyWithZone:(NSZone *)zone {
AccountSummary *newAccount = [[AccountSummary allocWithZone:zone] init];
newAccount.name = [self.name copyWithZone:zone];
newAccount.city = [self.city copyWithZone:zone];
newAccount.state = [self.state copyWithZone:zone];
newAccount.phone = [self.phone copyWithZone:zone];
newAccount.isLocal = self.isLocal;
return newAccount;
}
There's no leak here that I can see. There will, however, be quite a bit of peak memory usage.
The exact behaviour of things that should logically release memory varies. Quite often, instead of releasing the memory it's autorelased. (With MRR, there used to be a method called autorelease.) When you autorelease something, it isn't really released but is instead scheduled for release later, when your code is finished because it's returned to the main event loop.
If part of this is being autoreleased — and my guess is that the property assignment is autoreleasing, because autorelease is "safer" than hard releasing — that memory won't be deallocated until your next autoreleasepool flush. Code on the main thread has an autoreleasepool set up by the OS itself, so each time you return to the main event loop everything that's been autoreleased gets flushed out. Here, that probably means that all 10,000,000 copies are kept in memory until you return to the main event loop. Darn right that'll crash a real device. :)
(That's assuming you're on the main thread; if you're not, you may not even have an autorelasepool set up, which means you probably will get a leak. But I think you get warnings to console in this case, so you'd already have a hint about which way to go.)
You can reduce this peak memory usage by using #autoreleasepool:
for (i = 0; i < 10000000; i++) #autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
What will happen now is that the memory scheduled for release later in each iteration of the loop will actually be released each iteration of the loop. That should solve your immediate problem.
That said, i can't imagine why you're doing this except to check the behaviour. And if that's the case, this is unlikely your core problem.
Assuming your core problem is a leak, with ARC you're not really looking for leaks. You're looking for circular references. That means your answer likely lies elsewhere in your code. If you're sure it's self's accounts rather than dsl's accounts that are leaking, look for self being involved in a circular loop.
Also, keep in mind that calling copyWithZone: on a NSString will probably not copy the string. (There's no need to copy a read-only string, as the read-only string can't be changed. Both "copies" can be the same object.) So if you're leaking just strings, they could be associated with the original objects.
When creating lots of objects inside a loop, you should do that inside an auto release pool.
#autoreleasepool {
for (i = 0; i < 10000000; i++) {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}
or, more likely in the real world...
for (i = 0; i < 10000000; i++) {
#autoreleasepool {
self.accounts = [[NSArray alloc] initWithArray:[_dal accounts] copyItems:YES];
}
}

Proper Memory Management for Objective-C Method

I'm programming an iPhone app and I had a question about memory management in one of my methods. I'm still a little new to managing memory manually, so I'm sorry if this question seems elementary.
Below is a method designed to allow a number pad to place buttons in a label based on their tag, this way I don't need to make a method for each button. The method works fine, I'm just wondering if I'm responsible for releasing any of the variables I make in the function.
The application crashes if I try to release any of the variables, so I'm a little confused about my responsibility regarding memory.
Here's the method:
FYI the variable firstValue is my label, it's the only variable not declared in the method.
-(IBAction)inputNumbersFromButtons:(id)sender {
UIButton *placeHolderButton = [[UIButton alloc] init];
placeHolderButton = sender;
NSString *placeHolderString = [[NSString alloc] init];
placeHolderString = [placeHolderString stringByAppendingString:firstValue.text];
NSString *addThisNumber = [[NSString alloc] init];
int i = placeHolderButton.tag;
addThisNumber = [NSString stringWithFormat:#"%i", i];
NSString *newLabelText = [[NSString alloc] init];
newLabelText = [placeHolderString stringByAppendingString:addThisNumber];
[firstValue setText:newLabelText];
//[placeHolderButton release];
//[placeHolderString release];
//[addThisNumber release];
//[newLabelText release];
}
The application works fine with those last four lines commented out, but it seems to me like I should be releasing these variables here. If I'm wrong about that I'd welcome a quick explanation about when it's necessary to release variables declared in functions and when it's not. Thanks.
Yes, you need to release them, but you need them just a little longer than beyond the end of your function.
The solution is called autorelease. Just replace release with autorelease and the objects stay around until the program gets back to the runloop.
When the program gets back there, everybody interested in one of the objects should have sent a retain message to it, so the object will not be deallocated when released by the NSAutoreleasePool.
edit actually, looking at your code, there's a lot more wrong with it. E.g. this:
UIButton *placeHolderButton = [[UIButton alloc] init];
placeHolderButton = sender;
doesn't make sense. First you allocate an object, then assign (a pointer to) it to variable placeHolderButton. That's fine.
Then you assign sender to that same variable. The reference to the object you just created is now lost.
Not sure if I get what you want, but this would be better:
-(IBAction)inputNumbersFromButtons:(id)sender {
UIButton *placeHolderButton = sender; // this is still a little useless, but ok
int i = placeHolderButton.tag;
NSString *addThisNumber = [NSString stringWithFormat:#"%i", i];
NSString *placeHolderString = firstValue.text;
NSString *newLabelText = [placeHolderString stringByAppendingString:addThisNumber];
[firstValue setText:newLabelText];
}
No allocs, so no releases necessary. The strings returned by those functions are already added to the autoreleasepool, so they will be deallocated automatically (if needed).
Well. Release them when you are done with them. The sooner the better. Some objects are tricky if you are new to memory management.
Release them in the dealloc method then.
The auto release pool can be handy, some people might disagree according to the performance issues.
you need to release anything containing the word new, alloc/init or copy.
also, you don't need to alloc/init this:
UIButton *placeHolderButton = [[UIButton alloc] init];
placeHolderButton = sender;
another way of doing this is:
UIButton *placeHolderButton = (UIButton *)sender;
in your version, it is allocating an instance with a retain count of +1, but you are immediately replacing the reference, so there is no way of releasing the memory later.
you are creating a lot of instances with alloc/init, and then replacing their references with autoreleased instances.
you could use
NSString *placeHolderString = [placeHolderString stringByAppendingString:firstValue.text];
instead of
NSString *placeHolderString = [[NSString alloc] init];
placeHolderString = [placeHolderString stringByAppendingString:firstValue.text];
which is again replacing a manually managed instance created on the first line, with an autoreleased instance on the second.
infact you could replace every alloc/init in this with the factory method and not have to deal with memory at all in it as they would be autoreleased instances.
-(IBAction)inputNumbersFromButtons:(id)sender {
//cast sender as a UIButton to suppress compiler warning, and allow us to reference it as placeholder button
UIButton *placeHolderButton = (UIButton *) sender;
int i = placeHolderButton.tag;
NSString *addThisNumber = [NSString stringWithFormat:#"%i", i];
[firstValue setText:[firstValue.text stringByAppendingString:addThisNumber]];
}
If you look at the class docs for NSString, any method with a + next to it(ie +stringWithString:(NSString *)string) is a class method, don't use these methods on a reference after you have called alloc/init on it.
I find it puzzling that you use alloc/init on a UIButton.
I always use the factory methods, e.g.
UIButton* aButton = [UIButton buttonWithType:UIButtonTypeCustom];
This returns an autoreleased button which I immediately add to its intended parent view.
Can't confirm it right now, but it looks as if the SDK caches UIButton instances and performs some optimizations behind the scenes. Every time I tried to retain a UIButton ivar, performance has degraded (especially when there is many sub views on screen)

Objective C Array and Object Release

I have a newbie question regarding when to release the elements of a NSArray. See following pseudo code:
NSMutalbeArray *2DArray = [[NSMutableArray alloc] initWithCapacity:10];
for (int i=0;i<10;i++) {
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:5];
for (int j=0;j<5;j++) {
MyObject *obj = [[MyObject alloc] init];
[array addObject:obj];
[obj release];
}
[2DArray addObject:array];
[array release];
}
// use 2DArray to do something
[2DArray release]
My question here is, when I release 2DArray, do I need to explicitly release each of its element (array) first? Also, before I release the "array" object, do I need to release each of its element (MyObject) first?
I am new to Objective C. Please help. thanks.
No, you don't need to tell each object to be released. When you send a release method to an NSArray, it automatically sends a release method to each item inside first.
So in your case, you send [2DArray release]. This automatically sends [array release] to every other array, which sends [obj release] to each object inside each array.
You don't need to release the kept objects. NSArray retains them when you add, and releases them when released. So if you allocate, add to the array, then release, the object in the array will have the retain count of 1. Once the array is freed, the object is released, therefore freed.
When an object is created, it has a retain count of 1. Whenever a object is added to an array, its retain count is increased (in this case to 2). After adding to the array, your code release its hold of the object, dropping its retain count by 1 (to 1 in this case). Then when you release the array, it calls release on everything in it dropping their retain counts by 1 (to 0 in this case). When retain count hits 0 the object is deallocated.
Your code looks correct from a memory management stand point.

Memory Management for shared array elements

I am a little confused about releasing memory for array elements that are shared by multiple arrays. Here's the scenario:
Manager class has an instance variable NSMutableArray* mgrArray
Helper class has an instance variable NSMutableArray* helperArray.
Manager's init method:
NSMutableArray* mgrArray = [[[NSMutableArray alloc] init] autorelease];
for (int i=0; i<10; i++) {
Food *f = [[[Food alloc] initWithType:#"Fruit"] autorelease];
[mgrArray addObject:f];
}
Helper's init method:
NSMutableArray* helperArray = [[[NSMutableArray alloc] init] autorelease];
The manager object passes some of the mgrArray elements to Helper class to store for Helper's own access purposes (say for efficiency). Some Manager method that does this:
Food *e1 = [mgrArray objectAtIndex:3];
Food *e2 = [mgrArray objectAtIndex:5];
Food *e3 = [mgrArray objectAtIndex:7];
[helper. helperArray addObject:e1];
[helper. helperArray addObject:e2];
[helper. helperArray addObject:e3];
Question 1: when adding e1 to helperArray, should it be copied or retained or is it alright as written above?
Question 2: who should release the memory of the food objects and how?
If you put an object in an array, it retains it. If you remove an object from an array, the array releases it.
It is that simple; ownership -- retains -- should always be bounded by lines of encapsulation.
If you need to ensure that an object remains alive when you are using it, retain it. The one edge case is if you do something like:
foo = [someArray objectAtIndex: 0];
[someArray removeObjectAtIndex: 0];
At that point, foo may have been released. To fix:
foo = [someArray objectAtIndex: 0];
[foo retain];
[someArray removeObjectAtIndex: 0];
.. do stuff ..
[foo release];
Question 1: when adding e1 to
helperArray, should it be copied or
retained or is it alright as written
above?
Simply add e1 to the helperArray. Done.
Question 2: who should release
the memory of the food objects and
how?
When the array is released to the point of being dealloc'd, all objects contained within the array will be released (but not necessarily deallocated unless nothing else holds a retain).
An object will be retained when added to an array and released when removed from an array.
If you are using an autoreleased object there is nothing else to do.
If you are using a regular object, you can release it after it is added to the first array.