2013-11-14 3 views
7

Я открываю камеру с UIImagePickerControllerSourceTypeCamera и обычным cameraOverlayView, поэтому я могу сделать несколько фотографий без шага «Использовать фотографию».UIGraphicsGetImageFromCurrentImageContext() - Утечка памяти

Это прекрасно работает, но в функции сохранения фотографий есть утечка памяти. Благодаря большому количеству отладки и исследований я сузил ее до функции UIGraphicsGetImageFromCurrentImageContext.

Вот фрагмент кода, где это происходит:

UIGraphicsBeginImageContextWithOptions(timestampedImage.frame.size, timestampedImage.opaque, [[UIScreen mainScreen] scale]); 
[[timestampedImage layer] renderInContext:UIGraphicsGetCurrentContext()]; 
UIImage *finalTimestampImage = UIGraphicsGetImageFromCurrentImageContext(); 
UIGraphicsEndImageContext(); 

я рыскал по интернету и, кажется, что UIGraphicsGetImageFromCurrentImageContext() функция (цитата из this SO question) «возвращает новый autoreleased UIImage и указывает finalTimestampImage Ивар к нему ранее выделенному UIImage никогда не было выпущено, переменная к нему просто переадресована в другое место ».

Я пробовал так много решений, которые, по-видимому работали для других:

  • Добавление timestampedImage.layer.contents = nil; после UIGraphicsEndImageContext

  • CGContextRef context = UIGraphicsGetCurrentContext(); Добавление и CGContextRelease(context); после UIGraphicsEndImageContext

  • Обертывание выше фрагмент кода в NSAutoreleasePool

  • Обертывание всю saveThisPhoto функцию в NSAutoreleasePool

  • Создание NSAutoreleasePool, когда камера всплывает и вызова [pool release] когда didReceiveMemoryWarning называется

  • Закрытие всплывающего окна камеры при didReceiveMemoryWarning называется, надеясь, что это будет очистить бассейн

  • Каждая возможная комбинация из вышеперечисленных

Все, что я стараюсь, когда я фотографирую, я вижу, как Memory Utilized поднимается и не падает, когда я многократно фотографирую на устройстве.

Кто-нибудь знает, как я могу освободить объект автореферации, созданный UIGraphicsGetImageFromCurrentImageContext?

В качестве альтернативы, существует альтернативный способ сделать UIImage из UIImageView?

Edit:

Вот полные функции, как просили. Там добавлено много дополнительных выпусков, чтобы попробовать убедиться, что все очищено. Я прошел проверку и протестировал на утечку памяти с каждым блоком кода в saveThisPhoto систематически, и это происходит только тогда, когда выполняется блок UIGraphicsGetImageFromCurrentImageContext (фрагмент выше).

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { 

    NSLog(@"SAVING PHOTO"); 

    [self saveThisPhoto:info]; 

    picker = nil; 
    [picker release]; 

    info = nil; 
    [info release]; 

} 

- (void)saveThisPhoto:(NSDictionary *)info { 

    // Get photo count for filename so we're not overriding photos 

    int photoCount = 0; 

    if ([[NSUserDefaults standardUserDefaults] objectForKey:@"photocount"]) { 
     photoCount= [[[NSUserDefaults standardUserDefaults] objectForKey:@"photocount"] intValue]; 
     photoCount++; 
    } 

    [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%d", photoCount] forKey:@"photocount"]; 
    [[NSUserDefaults standardUserDefaults] synchronize]; 

    // Obtaining saving path 

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsDirectory = [paths objectAtIndex:0]; 
    NSString *fileName = [NSString stringWithFormat:@"ri_%d.jpg", photoCount]; 
    NSString *fileNameThumb = [NSString stringWithFormat:@"ri_%d_thumb.jpg", photoCount]; 
    NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:fileName]; 
    NSString *imagePathThumb = [documentsDirectory stringByAppendingPathComponent:fileNameThumb]; 

    // Extracting image from the picker and saving it 

    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType]; 

    // SAVE TO IPAD AND DB 

    if ([mediaType isEqualToString:@"public.image"]){ 

     // Get Image 

     UIImage *editedImage = [info objectForKey:UIImagePickerControllerOriginalImage]; 

     // Figure out image orientation 

     CGSize resizedSize; 
     CGSize thumbSize; 

     if (editedImage.size.height > editedImage.size.width) { 
      resizedSize = CGSizeMake(480, 640); 
      thumbSize = CGSizeMake(150, 200); 
     } else { 
      resizedSize = CGSizeMake(640, 480); 
      thumbSize = CGSizeMake(150, 113); 
     } 

     // MAKE NORMAL SIZE IMAGE 

     UIImage *editedImageResized = [editedImage resizedImage:resizedSize interpolationQuality:0.8]; 

     // clean up the one we won't use any more 

     editedImage = nil; 
     [editedImage release]; 

     // ADD TIMESTAMP TO IMAGE 

     // make the view 

     UIImageView *timestampedImage = [[UIImageView alloc] initWithImage:editedImageResized]; 
     CGRect thisRect = CGRectMake(editedImageResized.size.width - 510, editedImageResized.size.height - 30, 500, 20); 

     // clean up 

     editedImageResized = nil; 
     [editedImageResized release]; 

     // make the label 

     UILabel *timeLabel = [[UILabel alloc] initWithFrame:thisRect]; 
     timeLabel.textAlignment = UITextAlignmentRight; 
     timeLabel.textColor = [UIColor yellowColor]; 
     timeLabel.backgroundColor = [UIColor clearColor]; 
     timeLabel.font = [UIFont fontWithName:@"Arial Rounded MT Bold" size:(25.0)]; 
     timeLabel.text = [self getTodaysDateDatabaseFormat]; 
     [timestampedImage addSubview:timeLabel]; 

     // clean up what we won't use any more 

     timeLabel = nil; 
     [timeLabel release]; 

     // make UIIMage out of the imageview -- MEMORY LEAK LOOKS LIKE IT IS IN THIS BLOCK 

     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

     UIGraphicsBeginImageContextWithOptions(timestampedImage.frame.size, timestampedImage.opaque, [[UIScreen mainScreen] scale]); 
     [[timestampedImage layer] renderInContext:UIGraphicsGetCurrentContext()]; 
     UIImage *finalTimestampImage = UIGraphicsGetImageFromCurrentImageContext(); 
     UIGraphicsEndImageContext(); 
     timestampedImage.layer.contents = nil; 
     CGContextRef context = UIGraphicsGetCurrentContext(); 
     CGContextRelease(context); 

     // clean up the one we won't use any more 

     timestampedImage = nil; 
     [timestampedImage release]; 

     // SAVE NORMAL SIZE IMAGE TO DOCUMENTS FOLDER 

     NSData *webDataResized = UIImageJPEGRepresentation(finalTimestampImage, 1.0); // JPG 

     [webDataResized writeToFile:imagePath atomically:YES]; 

     // clean up the one we won't use any more 

     finalTimestampImage = nil; 
     [finalTimestampImage release]; 

     [pool release]; // to get rid of the context image that is stored 

     // SAVE TO DATABASE 

     [sqlite executeNonQuery:@"INSERT INTO inspection_images (agentid,groupid,inspectionid,areaid,filename,filenamethumb,filepath,orderid,type) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?) ", 
     [NSNumber numberWithInt:loggedIn], 
     [NSNumber numberWithInt:loggedInGroup], 
     myInspectionID, 
     [[tableData objectAtIndex:alertDoMe] objectForKey:@"areaid"], 
     fileName, 
     fileNameThumb, 
     documentsDirectory, 
     [NSNumber numberWithInt:photoCount], 
     [NSNumber numberWithInt:isPCR] 
     ]; 

     // Clean up 

     webDataResized = nil; 
     [webDataResized release]; 

    } else { 

     NSLog(@">>> IMAGE ***NOT*** SAVED"); 

    } 

    NSLog(@"IMAGE SAVED - COMPLETE"); 

    info = nil; 
    [info release]; 

} 
+0

Вы подтвердили, что проблема связана не с дальнейшим кодом, использующим изображение finalTimestampImage? Возможно, где-то есть цикл сохранения. – rmaddy

+0

'finalTimestampImage' используется для получения' UIImageJPEGRepresentation', который сохраняется на устройстве, тогда оба из них «выпущены». Я все еще убежден, что это скрытая 'UIImage' функция' UIGraphicsGetImageFromCurrentImageContext' создает, как упоминалось в другом вопросе SO. – Batnom

+0

Нет, вы неправильно поняли предыдущий вопрос SO. «Ранее выделенный UIImage» - это UIImage в '_renderImage = [[UIImage alloc] init];' который протекает в '_renderImage = UIGraphicsGetImageFromCurrentImageContext();'. Если вы объявите переменную, как в своем фрагменте, она не будет протекать. Возможно, вы могли бы разместить более полный образец кода. – HBu

ответ

1

Вы устанавливаете свои переменные в ноль, прежде чем выпускать их, а некоторые уже выпущены автоматически.

Обычно при использовании выпуска вы должны отпускать и устанавливать нулевое значение.

[var release] 
var = nil; 

Но в некоторых из них вы не должны вызывать выпуск.

Следующий ваш главный виновник.

// clean up the one we won't use any more 

    timestampedImage = nil; 
    [timestampedImage release]; 

    // SAVE NORMAL SIZE IMAGE TO DOCUMENTS FOLDER 
+0

Спасибо за это, не знаю, как я испортил нуль/порядок выпуска. Я только выпускаю 'timeLabel' и' timestampedImage', поскольку они являются теми, которые я вручную 'alloc'. Я могу теперь многократно снимать фотографии без каких-либо утечек - всего 225 фотографий одновременно и мог бы продолжаться! Еще раз спасибо! – Batnom

+1

В iOS8, используя ARC, как обращаться с «UIGraphicsGetImageFromCurrentImageContext()». Его всегда возвращается «ноль». – Satyam

Смежные вопросы