Releasing UIImageView and still getting a memory leak - iphone

I have searched my best before posting this here. below is the code that is causing memory leak. Including autorelease fixes the memory leak but I am more interested in knowing what am I doing wrong here. If i own it, I should release it, which is what I am trying to do :)
THankyou for the help in advance. ContainerView is a UIView, and I am adding my welcome text to it.
UIImageView *welcomeText = [[UIImageView alloc] init];
welcomeText = [[UIImageView alloc] initWithFrame:CGRectZero];
welcomeText.frame = (CGRect) { CGRectGetMidX(containerView.frame) -295 , 100.0, 590,134 };
welcomeText.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth;
welcomeText.image = [UIImage imageNamed:#"welcome-to-the-jungle.png"];
[containerView addSubview:welcomeText];
[welcomeText release];

UIImageView *welcomeText = [[UIImageView alloc] init];
welcomeText = [[UIImageView alloc] initWithFrame:CGRectZero];
After the 2nd line previously alloced welcomeText is lost and you are leaking that memory. After 1st line an image view is alloced and welcomeText is pointing to that. In 2nd line another image view is alloced and now welcomeText is pointing to this new one. So you don't have any pointer to 1st alloced image view and as a result that is leaked.
Here you don't need the 1st line actually.
UIImageView *welcomeText = [[UIImageView alloc] initWithFrame:CGRectZero];

You must not allocate an UIImageView object in the first line. In the second line, the object allocated in the first line leaks.

Related

How to set UIImageView frame in ViewController for Universal App?

For a Universal App, how do I set the UIImageView frame in the ViewController?
UIView *baseView = [[UIView alloc] init];
[self.view addSubview:baseView];
// Displays UIImageView
UIImageView *myImage = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"tool.png"]];
[baseView addSubview:myImage];
When I run app it shows black screen. So i guess I have to set a frame for it.
You kinda answered your own question - yes you have to set a frame for it.
You may want to try:
UIView *baseView = [[UIView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:baseView];
// Displays UIImageView
UIImageView *myImage = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"tool.png"]];
myImage.frame = baseView.bounds;
[baseView addSubview:myImage];
You could also set the autoresizingmask for baseView and myImage so that as self.view's frame changes, so do these two views.
Also, just in case you aren't using ARC - don't forget to autorelease these views before leaving this method.

XCode - Working with Build > Analyze, Potential Leak Warnings

I just discovered the Build > Analyze feature of XCode today so I am trying to go through and address all the errors it is finding. There are a few lines XCode finds exception with that are confusing me:
//Test View
self.imageViewTest = [[UIImageView alloc] init];
self.imageViewTest.frame = CGRectMake(0, 0, 100, 100); // <=== Leak
[self.view addSubview:self.imageViewTest];
//Test View 2
self.imageViewTestB = [[UIImageView alloc] init];
self.imageViewTestB.frame = CGRectMake(0, 100, 100, 100); // <=== Leak
[self.view addSubview:self.imageViewTestB];
and later in my setup of video capture
self.captureOutput = [[AVCaptureVideoDataOutput alloc] init];
captureOutput.alwaysDiscardsLateVideoFrames = YES; // <=== Leak
The warning at each of these lines is "Potential leak of an object". All 3 of these objects are sent the release message in my dealloc method. What could be wrong here?
Thanks!
If you are not using ARC and your properties are setup with the retain attribute, then yes, these are leaks. This line:
self.imageViewTest = [[UIImageView alloc] init];
should be:
UIImageView *iv = [[UIImageView alloc] init];
self.imageViewTest = iv;
[iv release];
or:
self.imageViewTest = [[[UIImageView alloc] init] autorelease];
Or better yet, use ARC. It makes things SO much easier.

UIImageView alloc] initWithImage using IBOutlet not working

I've typically followed the pattern of creating an object in my method (like viewDidLoad), have the property hold onto it, then release the original object created. So something like this:
NSArray *myArray = [NSArray alloc] initWithObjects:#"1", #"2", #"3", nil];
self.array = myArray;
[myArray release];
I thought I could do the same thing with UIImageView. I have a UIImageView created in my .xib. I have an outlet to it that is a property like so
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
In my viewDidLoad, I do:
UIImageView *imgView = [[UIImageView alloc] initWithImage;[UIImage imageNamed:#"arrow.png"]];
self.imageView = imgView;
[imgView release];
If I run this, I do not get anything on screen. However, if I do just this in my viewDidLoad instead:
[self.imageView setImage:[UIImage imageNamed:#"arrow.png"]];
I do get my arrow image on screen. Is there something different about UIImageView that I am missing here?
Edit because of first response: Is UIImageView different than UITableView in that I need to add it as a subview to my current view? When I create a UITableView in IB, I do not add it as a subview in viewDidLoad.
Thanks.
You are missing the point behind how IBOutlet work. If something is specified in .xib then it is already allocated for you :) You just use it.
Hence [self.imageView setImage:[UIImage imageNamed:#"arrow.png"]]; works since you just assigned an image to it.
However, when doing
UIImageView *imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrow.png"]];
self.imageView = imgView;
[imgView release];
you actually deallocated the imageView instance created by IB at the self.imageView = imgView; line and set a new one and neither did you added it as subview to anything. Since it is a new instance it will need to be added !! But anyways this approach is wrong if you are using IBOutlet
You need to add self.imageView to your view hierarchy.
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView = ...
// You are missing this part!
[self.view addSubview:self.imageView];
...
}
PS. You dont need to mark your UIImageView property as IBOutlet since you are not using Interface Builder to build your view.

How to add two uiscrollviews as subview

After lot of research and working for days, I really need some guidance here. I am trying to add uiscrollview on top of each other. I have two UIScrollViews: ImageScrollView and MarkScrollView. I want to add MarkScrollView as subview to ImageScrollView. ImageScrollView is added to UIViewController MapImageViewController.
In ImageScrollView it consists of images which are in tiledLayer. In MarkScrollView, it consists of imageview which has a pointer image. But for some reason it cannot be added as subview to other view. I have added my code. If anybody can help me to figure out the error it will be really appreciated.
/** ImageScrollView.m It adds uiview on top of ImageScrollView***/
UIView *imageView = [[TileView alloc] initWithImageName:imageName size:imageSize];
[(TileView *)imageView setAnnotates:NO]; // ** remove this line to remove the white tile grid **
[self addSubview:imageView];
/** MarkScrollView.m It adds uiview with uiimages on top of MarkScrollView**/
UIImage *markerimage = [UIImage imageNamed:#"map.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage: markerimage];
imageView.image = markerimage;
[imageView setFrame:CGRectMake(100,100,50,50)];
imageview = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)] autorelease];
[imageview setBackgroundColor:[UIColor blackColor]];
[imageview addSubview: imageView];
[self addSubview:imageview];
/** MapImageViewController.m It adds ImageScrollView on top of another UIScrollView pagingScrollView **/
UIScrollView *pagingScrollView;
ImageScrollView *page = [[[ImageScrollView alloc] init] autorelease];
[pagingScrollView addSubview:page];
MarkScrollView *markerScroll = [[[MarkScrollView alloc] init] autorelease];
[page addSubview:markerScroll]; /**But it never adds the other scrollview here **/
/** MapImageViewController.h **/
#interface MapImageViewController : UIViewController <UIScrollViewDelegate, UIGestureRecognizerDelegate> {
ImageScrollingView *imageScrollView;
MarkerIconsScrollView *markerScrollView;
}
UIScrollView *pagingScrollView;
ImageScrollView *page = [[[ImageScrollView alloc] init] autorelease];
[pagingScrollView addSubview:page];
MarkScrollView *markerScroll = [[[MarkScrollView alloc] init] autorelease];
[page addSubview:markerScroll]; /**But it never adds the other scrollview here **/
I'm noticing some things on this code snippet:
pagingScrollView is never initialized
the views that you are adding to it (provided that it actually is initialized with a valid frame), are not initialized with a frame. Always use - (id)initWithFrame:(CGRect)aRect to initialize your UIViews
Good luck!

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?
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];
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.
[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.