Reputation: 3506
Okay so im finally to the point where I am testing my iPad App on an actual iPad...
One thing that my app does is display a large (2mb) image in a scroll view. This is causing the iPad to get memory warnings. I run the app in the instruments to check for the leak.
When I load the image, a leak is detected and i see the following in the allocations:
ALl Allocations: 83.9 MB Malloc 48.55 MB: 48.55 MB Malloc 34.63 MB: 34.63 MB
What im trying to understand is how to plug the leak obviously, but also why a 2MB image is causing a malloc of 20x that size
I am very new to programming in obj-c so im sure this is an obvious thing, but I just cant figure it out. Here is the code:
@interface ChartsViewController : UIViewController <UIScrollViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource> {
IBOutlet UIScrollView *scrollView;
UIImageView *imageView;
NSString *chart;
NSString *chartFile;
UIPickerView *picker;
NSDictionary *chartsDictionary;
NSArray *chartTypes;
NSArray *charts;
IBOutlet UILabel *chartNameLabel;
IBOutlet UIActivityIndicatorView *activityIndicator;
}
@property (nonatomic, retain) UIScrollView *scrollView;
@property (nonatomic, retain) UIImageView *imageView;
@property (nonatomic, retain) NSString *chart;
@property (nonatomic, retain) NSString *chartFile;
@property (nonatomic, retain) IBOutlet UIPickerView *picker;
@property (nonatomic, retain) NSDictionary *chartsDictionary;
@property (nonatomic, retain) NSArray *chartTypes;
@property (nonatomic, retain) NSArray *charts;
@property (nonatomic, retain) IBOutlet UILabel *chartNameLabel;
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView *activityIndicator;
-(IBAction) chartSelected;
- (void)alertView:(UIAlertView *)actionSheet
///////////////////////////////
-(IBAction) chartSelected {
[imageView removeFromSuperview];
imageView = nil;
chartNameLabel.text = @"";
NSInteger chartTypeRow = [picker selectedRowInComponent:kChartTypeComponent];
NSInteger chartRow= [picker selectedRowInComponent:kChartComponent];
chart = [self.charts objectAtIndex:chartRow];
chartFile = [chart stringByReplacingOccurrencesOfString:@" " withString:@"_"];
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *tempString = [[NSString alloc]initWithFormat:@"%@/%@.jpg",docsPath,chartFile];
NSData *temp = [NSData dataWithContentsOfFile:tempString];
if (temp != NULL){
temp = nil;
[imageView removeFromSuperview];
imageView = nil;
UIImageView *tempImage = [[UIImageView alloc]initWithImage:[UIImage imageWithContentsOfFile: tempString]];
[tempString release];
self.imageView = tempImage;
scrollView.contentSize = CGSizeMake(imageView.frame.size.width , imageView.frame.size.height);
scrollView.maximumZoomScale = 4.0;
scrollView.minimumZoomScale = .05;
scrollView.clipsToBounds = YES;
scrollView.delegate = self;
scrollView.zoomScale = .3;
[scrollView addSubview:imageView];
[tempImage release];
imageView = nil;
chartNameLabel.text = chart;
}
else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Download Chart"
message:@"It appears that you have not yet downloaded this chart. Press OK to download this chart to your iPad. Depending on your internet connection, the download could take several minutes."
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:@"Cancel", nil];
[alert show];
[alert release];
}
}
- (void)dealloc {
[imageView release];
[scrollView release];
[chartsDictionary release];
[picker release];
[chartTypes release];
[charts release];
[super dealloc];
}
Upvotes: 2
Views: 3269
Reputation: 52538
Would be a good idea to switch to ARC. Alternatively, build with the static analyser and fix all the warnings that it gives you. It is quite good at that.
It seems that you sometimes use imageView and sometimes self.imageView. That indicates that your instance variable is imageView and not _imageView. That's a huge source of bugs. If imageView is a (release) property, self.imageView = nil releases it, but imageView = nil doesn't. I strongly suggest starting all instance variables with an underscore so that you only access them intentionally.
Upvotes: 0
Reputation: 75058
You say you have a 2MB image. But that means a 2MB JPG, right?
So what is the size in pixels - because when you load the image into memory, it must be de-compressed. And that means it will be horizontal resolution * vertical resolution * 8 * 4 (alpha channel) bytes in memory
.
That is why you are seeing 20-30MB allocated every time you load the image, regardless of retain issues (which just mean each 30MB allocated would not be released).
Upvotes: 3
Reputation: 86651
tempImage is still leaking.
replace the line that says
imageView = nil;
with
[self setImageView: nil];
or
self.imageView = nil;
Upvotes: 2
Reputation:
Lots of leaks in this code.
For every object created using alloc
, you need to release it at some point when you are done using it.
The following items are being leaked and need to be released
tempString
tempImage
alert
Also, you don't need that NSAutoreleasePool
, one is created for you by the cocoa framework before the event that calls your IBAction
is called and is drained with the method finishes. Also the autorelease pool is also only responsible for items that have been placed in it, which include anything that you send a autorelease
message to and any objects you get back from a method other than alloc
, new
, or one with copy
in the title.
Also, know that setting a local variable to nil is not the same as releasing it.
For example the creating the image should be
UIImageView *tempImage = [[UIImageView alloc]initWithImage:[UIImage imageWithContentsOfFile: tempString]];
self.imageView = tempImage;
[tempImage release];
Edit:
One more thing. When you access imageview
without using self.imageview
you are accessing the ivar directly and not through the property. So when you do self.imageview = tempImage
, it retains the image view as it should, but when you do imageview = nil
it nils out the reference without releasing the memory. This is another leak. Try self.imageview = nil
instead.
As to why it is so much memory, this I do not know unless it is related to expanding the image to its full size (by pixels), and not its compressed jpg size, or to other data being leaked along with the UIImageView object.
Upvotes: 2
Reputation: 53659
Both of the following lines will retain the UIImageView allocated as tempImage.
self.imageView = tempImage;
[scrollView addSubview:imageView];
add this line after addSubview:
[tempImage release];
You do not need imageView as a member unless you are manipulating it later. If you keep it, be sure to release it in dealloc. In general, every property marked retain should be released in dealloc unless you have some specific reason not to.
I usually do not make properties or even have members for views that will live in the view hierarchy unless I need to manipulate them, for example changing the text of a UILabel or the state of a UIButton.
Upvotes: 0