Получение сбоя после выбора изображений из UIImagePickerController (связано с утечкой памяти?) - PullRequest
3 голосов
/ 15 июня 2010

Я пытался минимизировать объем памяти, используя UIImagePickerController, но начинаю думать, что проблемы с памятью возникают из-за плохого управления памятью, а не из-за особого способа обработки объекта UIImagePickerController .

Мой рабочий процесс такой: нажата кнопка «Редактировать изображение», которая представляет UIActionSheet. Этот лист действий позволяет вам удалить, сделать снимок, выбрать из библиотеки или отменить. Если вы выберите «Выбрать из библиотеки» или «Сделать снимок», я alloc создаю экземпляр UIImagePickerController и представляю его, а затем выпускаю UIImagePickerController:

-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (actionSheet.tag != 999) {
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        imagePicker.delegate = self;

        BOOL pickImage = nil;

        if (actionSheet.tag == iPhoneWithDelete) {
            switch (buttonIndex) {
                case 0:
                    object.objectImage = nil;
                    pickImage = NO;
                    break;
                case 1:
                    imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
                    pickImage = YES;
                    break;
                case 2:
                    imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
                    pickImage = YES;
                    break;
                default:
                    pickImage = NO;
                    break;
            }
        } else if (actionSheet.tag == iPhoneNoDelete) {
            switch (buttonIndex) {
                case 0:
                    imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
                    pickImage = YES;
                    break;
                case 1:
                    imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
                    pickImage = YES;
                    break;
                default:
                    pickImage = NO;
                    break;
            }       
        } else if (actionSheet.tag == iPodWithDelete) {
            switch (buttonIndex) {
                case 0:
                    object.objectImage = nil;
                    pickImage = NO;
                    break;
                case 1:
                    imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
                    pickImage = YES;
                    break;
                default:
                    pickImage = NO;
                    break;
            }
        } else if (actionSheet.tag == iPodNoDelete) {
            switch (buttonIndex) {
                case 0:
                    imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
                    pickImage = YES;
                    break;
                default:
                    pickImage = NO;
                    break;
            }
        }

        if (pickImage) {
            imagePicker.allowsEditing = YES;
            [self presentModalViewController:imagePicker animated:YES];
        } else {
            [self setupImageButton];
            [self setupChooseImageButton];
        }
        [imagePicker release];
    }
}

Как только я получаю выделение из UIImagePickerController, я сохраняю 2 изображения, измененную версию отредактированного изображения для использования в качестве миниатюры и версию исходного неотредактированного изображения 800x600 в атрибуте отношения (Преобразование с использованием то же самое преобразование UIImage в PNG, которое можно найти в демонстрационном коде Recipes) для отображения: (методы изменения размера основаны на том, который показан в этой публикации SO .)

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

    [self dismissModalViewControllerAnimated:YES];

    NSManagedObject *oldImage = object.imageFull;
    if (oldImage != nil)
    {
        [object.managedObjectContext deleteObject:oldImage];
    }

    NSManagedObject *image = [NSEntityDescription insertNewObjectForEntityForName:@"Image" inManagedObjectContext:object.managedObjectContext];
    object.imageFull = image;

    UIImage *rawImage = [info objectForKey:@"UIImagePickerControllerOriginalImage"];

    CGSize size = CGSizeMake(800, 600);

    UIImage *fullImage = [UIImageManipulator scaleImage:rawImage toSize:size];

    [image setValue:fullImage forKey:@"imageFull"];

    UIImage *processedImage = [UIImageManipulator scaleImage:[info objectForKey:@"UIImagePickerControllerEditedImage"] toSize:CGSizeMake(75, 75)];
    object.objectImage = processedImage;
    [self setupImageButton];
    [self setupChooseImageButton];

    rawImage = nil;
    fullImage = nil;
    processedImage = nil;
}

Когда я прохожу через viewDidUnload, я устанавливаю self.object = nil и [object release] во время dealloc, но я все еще получаю предупреждения памяти после примерно 10 изменений изображения, с падением около 20. Это приводит меня полагать, что я не вывожу это полное изображение из памяти правильным способом. Что мне здесь не хватает?

А во-вторых, источник камеры использует значительно больше памяти, чем источник фотоальбомов? Я имею тенденцию получать больше сбоев при использовании камеры.

- EDIT -

Начало награды за любую информацию о том, что я могу неправильно обработать. Я буду обновлять этот пост любыми ответами на все, что мне неясно. Просто в конце моего ума на этом.

- РЕДАКТИРОВАТЬ 2 -

Переработан код, учитывающий предложения chrissr, и реализован GCD для повышения удобства использования. Это так эффективно, как этот процесс? По-прежнему получают предупреждения о памяти и вылетает около 20 изображений. Я уверен, что сочетание дорогого UIImage изменения размера и использования UIImagePickerController убивает процессор, но я не могу себе представить, что каждое приложение имеет дело с неопределенностью вокруг UIImagePickerController. Моя память занимает около 2 мегабайт. Я действовал в предположении, что это было много накладных расходов. Должен ли я уменьшить этот след еще больше?

Вот модифицированный код:

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

    [self dismissModalViewControllerAnimated:YES];

    if (object.imagePath != nil) {
        [self deleteImages];
    }
    dispatch_queue_t image_queue;
    image_queue = dispatch_queue_create("com.gordonfontenot.app", NULL);

    dispatch_async(image_queue, ^{

        NSDate *now = [NSDate date];

        NSDateFormatter *f = [[NSDateFormatter alloc] init];
        [f setDateFormat:@"yyyyMMDDHHmmss"];

        NSString *imageName = [NSString stringWithFormat:@"Image-%@-%i", [f stringFromDate:now], arc4random() % 100];
        NSString *thumbName = [NSString stringWithFormat:@"%@-thumb", imageName];

        [f release];

        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];

        NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:imageName];
        NSString *thumbPath = [documentsDirectory stringByAppendingPathComponent:thumbName];

        NSData *thumbImageData = UIImagePNGRepresentation([UIImageManipulator scaleImage:[info objectForKey:@"UIImagePickerControllerEditedImage"] toSize:CGSizeMake(120, 120)]);
        [thumbImageData writeToFile:thumbPath atomically:NO];
        dispatch_async(dispatch_get_main_queue(), ^{
            object.thumbPath = thumbPath;
            [self setupImageButton];
            imageButton.enabled = NO;
            [self setupChooseImageButton];
        });
        NSData *fullImageData = UIImagePNGRepresentation([UIImageManipulator scaleImage:[info objectForKey:@"UIImagePickerControllerOriginalImage"] toSize:CGSizeMake(800, 600)]);
        [fullImageData writeToFile:fullPath atomically:NO];

        dispatch_async(dispatch_get_main_queue(), ^{
            imageButton.enabled = YES;
            object.imagePath = fullPath;
        });

        if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
            UIImageWriteToSavedPhotosAlbum([info objectForKey:@"UIImagePickerControllerOriginalImage"], self, nil, nil);
        }

    });
    dispatch_release(image_queue);
}

1 Ответ

2 голосов
/ 30 июня 2010

Предупреждения в памяти чрезвычайно распространены при работе с UIImagePickerController.Это особенно актуально при использовании камеры.Помните, что хотя размер файла JPG или PNG на диске может составлять всего несколько МБ, растровое изображение без сжатия в памяти, используемое для рисования изображения, использует значительно больше.

Нет ничего, что вы делаете неправильно, нонекоторые улучшения могут быть сделаны:

Вместо того, чтобы хранить байты изображения в Core Data, почему бы не записать образ на диск и не сохранить путь к файлу в вашей базе данных?

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

Лучше всего записать изображения на диск как можно скорее после обработки и освободить память, которую они используют.с помощью.Затем сохраните их местоположение с использованием Core Data, а не необработанных данных.

...