утечки памяти iphone и malloc? - PullRequest
       19

утечки памяти iphone и malloc?

2 голосов
/ 23 апреля 2010

Хорошо, теперь я наконец приступил к тестированию своего приложения для iPad на реальном iPad ...

Одна вещь, которую делает мое приложение, это отображение большого (2 МБ) изображения в режиме прокрутки. Это заставляет iPad получать предупреждения памяти. Я запускаю приложение в приборах, чтобы проверить утечку.

Когда я загружаю изображение, обнаруживается утечка, и я вижу следующее в распределении:

ALl Распределение: 83,9 МБ Malloc 48,55 МБ: 48,55 МБ Malloc 34,63 МБ: 34,63 МБ

Что я пытаюсь понять, так это то, как, очевидно, как устранить утечку, но также и почему изображение размером 2 МБ вызывает маллок в 20 раз больше

Я очень новичок в программировании на obj-c, так что я уверен, что это очевидная вещь, но я просто не могу понять это. Вот код:

@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];
}

Ответы [ 5 ]

3 голосов
/ 23 апреля 2010

Вы говорите, что у вас есть изображение 2 МБ. Но это означает, что 2 МБ JPG, верно?

Так, каков размер в пикселях - потому что когда вы загружаете изображение в память, оно должно быть распаковано. А это значит, что это будет horizontal resolution * vertical resolution * 8 * 4 (alpha channel) bytes in memory.

Именно поэтому вы видите, что 20-30 МБ выделяются при каждой загрузке образа, независимо от проблем с сохранением (что означает, что каждые 30 МБ выделенные не будут освобождены).

2 голосов
/ 23 апреля 2010

tempImage все еще протекает.

замените строку с надписью

imageView = nil;

на

[self setImageView: nil];

или

self.imageView = nil;
2 голосов
/ 23 апреля 2010

Много утечек в этом коде.

Для каждого объекта, созданного с использованием alloc, вам необходимо освободить его в какой-то момент, когда вы закончите с его использованием.

Следующие предметы просочились и должны быть освобождены

  1. tempString
  2. tempImage
  3. alert

Кроме того, вам не нужно это NSAutoreleasePool, оно создается для вас каркасом какао до вызова события, которое вызывает ваш IBAction, и сливается с завершением метода. Кроме того, пул авто-выпусков также отвечает только за элементы, которые были помещены в него, включая все, что вы отправляете в autorelease сообщение, и любые объекты, которые вы возвращаете другим способом, кроме alloc, new или с copy в заголовке.

Кроме того, знайте, что установка локальной переменной на ноль - это не то же самое, что освобождение ее.

Например, создание изображения должно быть

UIImageView *tempImage = [[UIImageView alloc]initWithImage:[UIImage imageWithContentsOfFile: tempString]];
self.imageView = tempImage;
[tempImage release];

Edit:

Еще одна вещь. При доступе к imageview без использования self.imageview вы получаете доступ к ивару напрямую, а не через свойство. Поэтому, когда вы делаете self.imageview = tempImage, он сохраняет вид изображения, как и должен, но когда вы делаете imageview = nil, он обнуляет ссылку, не освобождая память. Это еще одна утечка. Попробуйте self.imageview = nil вместо.

Относительно того, почему это так много памяти, этого я не знаю, если это не связано с расширением изображения до его полного размера (в пикселях), а не с его сжатым размером JPG, или с другими данными, которые просочились вместе с UIImageView объект.

0 голосов
/ 24 февраля 2014

Было бы неплохо перейти на ARC. В качестве альтернативы, соберите статический анализатор и исправьте все предупреждения, которые он вам дает. Это довольно хорошо в этом.

Кажется, что вы иногда используете imageView, а иногда self.imageView. Это означает, что вашей переменной экземпляра является imageView, а не _imageView. Это огромный источник ошибок. Если imageView является свойством (release), self.imageView = nil освобождает его, а imageView = nil - нет. Я настоятельно рекомендую начинать все переменные экземпляра с подчеркивания, чтобы вы могли обращаться к ним только намеренно.

0 голосов
/ 23 апреля 2010

Обе следующие строки сохранят UIImageView, выделенный как tempImage.

self.imageView = tempImage;
[scrollView addSubview:imageView];

добавить эту строку после addSubview:

[tempImage release];

Вам не нужен imageView в качестве члена, если вы не манипулируете им позже. Если вы сохраните его, обязательно выпустите его в dealloc. Как правило, каждое свойство, помеченное как retain, должно быть освобождено в dealloc, если только у вас нет особых причин не делать этого.

Обычно я не создаю свойства и даже не имею членов для представлений, которые будут жить в иерархии представлений, если только мне не нужно манипулировать ими, например, изменять текст UILabel или состояние UIButton.

...