Brodie
Brodie

Reputation: 3506

iphone memory leaks and malloc?

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

Answers (5)

gnasher729
gnasher729

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

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

JeremyP
JeremyP

Reputation: 86651

tempImage is still leaking.

replace the line that says

imageView = nil;

with

[self setImageView: nil];

or

self.imageView = nil;

Upvotes: 2

user120587
user120587

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

  1. tempString
  2. tempImage
  3. 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

drawnonward
drawnonward

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

Related Questions