XCode - Working with Build > Analyze, Potential Leak Warnings - iphone

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.

Related

Zbar cameraOverlay UIBarItem click does not work

I've following code running on iPad, I'm usin Zbar 1.2.2 beta version for support in iPad.
ZBarReaderViewController* mReader = [[ZBarReaderViewController alloc] init];
mReader.readerDelegate = self;
mReader.showsZBarControls = NO;
mReader.wantsFullScreenLayout = NO;
mReader.readerView.frame = CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height);
UIButton *imageView = [[UIButton alloc] init];
imageView.frame = CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height);
[self setupToolBar:imageView];
mReader.cameraOverlayView = imageView;
setupToolBar does following
- (void)setupToolBar:(UIButton*)imageView
{
[imageView retain];
UIToolbar* imagePickerToolBar = [[UIToolbar alloc] init];
UIImage *c = [UIImage imageNamed:#"close.png"];
UIBarButtonItem *closeItem = [[UIBarButtonItem alloc] initWithImage:c style:UIBarButtonItemStylePlain target:self action:#selector(closeBarItemPressed:)];
NSArray *items = [NSArray arrayWithObjects: closeItem, nil];
[imagePickerToolBar setItems:items animated:NO];
imagePickerToolBar.frame = CGRectMake(0, UIScreen.mainScreen.bounds.size.height - imagePickerToolBar.frame.size.height, imagePickerToolBar.frame.size.width, imagePickerToolBar.frame.size.height);
[imageView addSubview:imagePickerToolBar];
[imageView bringSubviewToFront:imagePickerToolBar];
[imagePickerToolBar release];
[closeItem release];
[imageView release];
}
Now my question : closeBarItemPressed: is not getting called, why and how to fix it ?
I had this problem too. It looks like touches are ignored in the bottom part of the overlay view. Try moving the button a little bit higher and it will work. (in your case if you change toolbar frame to be in the top of the screen, I bet your method will be called, at least this is what happened to me). Not sure why this is happening though.
Basically I solved my problem, by recompiling Zbar, with modified iPad settings. Now it works OK.

Will releasing an NSMutableArray having allocated UIViews release the UIViews as well?

Ok so here's what I'm doing.
NSMutableArray *array = [[NSMutableArray alloc] init];
UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
UIView *tempview2 = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView2];
[array release];
Will the releasing of the array, release the two allocated UIViews as well?
If you copy, alloc, retain, or new something, you are responsible for sending it either release or autorelease.
You said [[UIView alloc] init...] so you must release the resulting object.
You are responsible for releasing the views since you created them. Here is how it goes:
You create the views with a retain count of 1.
When they are added to the array, it will retain them (retain count = 2).
When you release the array, it will release the views (retain count = 1).
You still need to release them.
The correct code would be:
NSMutableArray *array = [[NSMutableArray alloc] init];
UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView];
[tempview release];
UIView *tempview2 = [[UIView alloc] initWithFrame:CGRectMake(15, 30, 320, 460)];
[array addObject:tempView2];
[tempview2 release];
[array release];

Releasing UIImageView and still getting a memory leak

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.

Why is this NSArray not working? I'm trying to add objects to it

I'm trying to add objects to this NSArray (labelArray) but for some reason, it returns as (null) in NSLog every time, and the count remains at 0.
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(howFarAlong, howFarDown, 50, 70)];
label.text = #"text";
[self.view addSubview:label];
[labelArray addObject:label];
NSLog(#"%#", labelArray);
[label release];
An NSArray is immutable. If you want to call -addObject:, use NSMutableArray. If labelArray is an NSArray, then that should crash. If it doesn’t crash, then it’s probably nil, and you haven’t initialized it. Some code that will work:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(howFarAlong, howFarDown, 50, 70)];
label.text = #"text";
[self.view addSubview:label];
if (labelArray == nil) {
labelArray = [[NSMutableArray alloc] init];
}
[labelArray addObject:label];
NSLog(#"%#", labelArray);
[label release];
You need to use an NSMutableArray if you want to change the data in your array. NSArray can only be used to create static arrays.
You probably also receive a message from the compiler stating that NSArray may not respond to 'addObjext'. This is your clue that the object you are using won't perform the requested selector (method). In this case, you are trying to change an immutable object, which won't work. You need to use an NSMutableArray. I suggest you read up on the differences in Apple's documentation.
I tested the code below. The count is 1 after the lable is added.
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(howFarAlong, howFarDown, 50, 70)];
label.text = #"text";
[self.view addSubview:label];
NSArray *labelArray = [NSArray arrayWithObject:label];
NSLog(#"Count: %d", labelArray.count);

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.