Nathan
Nathan

Reputation: 1

Why is my addSubview: method causing a leak?

Okay, so I have done a ton of research on this and have been pulling my hair out for days trying to figure out why the following code leaks:

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:@"%@%@%@",@"http://url/",comicNumber,@".png"]];
self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:@"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

Both self.imageView and self.scrollView are @propety (nonatomic, retain) and released in my dealloc.. imageView isn't used anywhere else in the code. This code is also run in a thread off of the main thread. If I run this code on my device, it will quickly run out of memory if I continually load this view. However, I've found if I comment out the following line:

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:@"%@%@%@",@"http://url/",comicNumber,@".png"]];
self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];
//[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:@"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

Memory usage becomes stable, no matter how many times I load the view. I have gone over everything I can think to see why this is leaking, but as far as I can tell I have all my releases straight. Can anyone see what I am missing?

Upvotes: 0

Views: 1400

Answers (3)

bbum
bbum

Reputation: 162712

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

Fine.

UIImage *comicImage = [self getCachedImage:[NSString stringWithFormat:@"%@%@%@",@"http://url/",comicNumber,@".png"]];

Don't call methods get* anything unless you are following standard Cocoa patterns (which this method is not). Just call it cachedImage:.

self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];

You are missing an alloc call; that should be:

self.imageView = [[[UIImageView alloc] initWithImage:comicImage] autorelease];

Or (if you want to avoid the autorelease pool; probably not an issue here):

UIImageView *iV = [[UIImageView alloc] initWithImage:comicImage];
self.imageView = iV;
[iV release];

[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = self.imageView.frame.size;
self.imageWidth = [NSString stringWithFormat:@"%f",imageView.frame.size.width];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

All fine. If there is a leak, it is either because imageView isn't released in dealloc or something else is hanging on to it (scrollView not being released, perchance?). Instruments can do a wonderful job of tracking down leaks, etc....


What the "leaks" tool looks for is objects that no longer have any references to them. In this case, it is quite likely that you have references remaining.

Frankly, given that you are easily able to reproduce the growth through repetition, Heapshot analysis will quite likely be highly applicable.

I wrote up a guide on Heapshot analysis a bit ago.

Upvotes: 0

ludesign
ludesign

Reputation: 1373

When calling this line:

[self.scrollView addSubview:self.imageView];

self.imageView is retained by its super view and when you don't need imageView anymore you should call:

[self.imageView removeFromSuperview];

This will call release on self.imageView.

p.s. You can track your ref counts by calling

NSLog(@"RefCount: %d", [self.imageView retainCount]);

add this line above

self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];

to track the refCount. (Better option is to use Instruments but you already know that :))

Edit: It is a good practice to [[alloc] init] objects when you have retain properties like this:

UIView *myView = [[UIView alloc] init];
self.myCustomView = myView;
[myView release];

Otherwise you'll get self.myCustomView retained twice.

Upvotes: 0

jakeva
jakeva

Reputation: 2835

You autorelease your imageview upon init, and then retain it by assigning it to self.imageView, then adding it as a subview retains it again. So, when the pool is drained, it gets a release message. When it is removed as a subview it gets a release message. Then if you dealloc, it gets a third release message. One of those three is not occurring. You say it's released in dealloc, so that's not it. The autorelease pool can be trusted to drain at some point, so that's not it. I would either make sure to remove it as a subview at some point, or get rid of one of your retain calls.

And.. shouldn't this:

self.imageView = [[[UIImageView initWithImage:comicImage] autorelease];

be this?:

self.imageView = [[UIImageView alloc] initWithImage:comicImage];

Upvotes: 1

Related Questions