使用UIImagePickerController保存的图像进行内存管理

Mic*_*lum 8 memory cocoa-touch objective-c uiimagepickercontroller ios

我正在编写一个应用程序,其中用户自己拍摄照片,然后通过一系列视图使用导航控制器调整图像.如果用户使用前置摄像头拍摄照片(在支持它的设备上设置为默认设置),则此功能完全正常,但是当我重复此过程时,我会大约一半通过并在发出内存警告后崩溃.

在使用仪器进行分析后,我发现当使用较低分辨率的前置摄像头图像时,我的应用程序内存占用大约保持在20-25 MB,但是当使用后置摄像头时,每次视图更改都会增加33 MB左右,直到它崩溃大约350 MB(在一个4S)

下面是我用来处理将照片保存到文档目录,然后读取该文件位置以将图像设置为a的代码UIImageView.此代码的"读取"部分通过多个视图控制器(viewDidLoad)进行回收,以设置我在每个视图中保存为背景图像的图像.

我删除了所有的图像修改代码,将其剥离到试图隔离问题的熊最小值,我似乎无法找到它.现在,所有应用程序都会在第一个视图中拍摄照片,然后将该照片用作大约10个以上视图的背景图像,并在用户浏览视图堆栈时进行分配.

现在显然分辨率越高的照片会占用更多的内存,但我不明白的是为什么低分辨率的照片似乎没有使用越来越多的内存,而高分辨率的照片不断使用越来越多直到崩溃.

我如何保存和阅读图像:

- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];    
    jpgData = UIImageJPEGRepresentation(image, 1);

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    NSString *documentsPath = [paths objectAtIndex:0];
    filePath = [documentsPath stringByAppendingPathComponent:@"image.jpeg"];
    [jpgData writeToFile:filePath atomically:YES];    

    [self dismissModalViewControllerAnimated:YES];
    [disableNextButton setEnabled:YES];

    jpgData = [NSData dataWithContentsOfFile:filePath];
    UIImage *image2 = [UIImage imageWithData:jpgData];
    [imageView setImage:image2];
}
Run Code Online (Sandbox Code Playgroud)

现在我知道我可以在保存之前尝试缩放图像,我计划在下一步查看,但我不明白为什么这不起作用.也许我错误地认为ARC在离开堆栈顶部时会自动释放视图及其子视图.

任何人都可以解释为什么我存储我的设备内存?(希望简单的事情,我完全可以忽略)我是不是设法将ARC扔出窗外?

编辑:我如何在其他视图中调用图像

- (void)loadBackground
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);  
    NSString *documentsPath = [paths objectAtIndex:0];
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.jpeg"];
    UIImage *image = [UIImage imageWithContentsOfFile:filePath];
    [backgroundImageView setImage:image];

}
Run Code Online (Sandbox Code Playgroud)

如何建立我的视图控制器之间的导航:

在此输入图像描述

编辑2:

我的基本声明是什么样的:

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface PhotoPickerViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
{
    IBOutlet UIImageView *imageView;
    NSData *jpgData;
    NSString *filePath;
    UIImagePickerController *imagePicker;
    IBOutlet UIBarButtonItem *disableNextButton;
}


@end
Run Code Online (Sandbox Code Playgroud)

如果相关,我如何调用我的图像选择器:

- (void)callCameraPicker
{


    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == YES)
    {
        NSLog(@"Camera is available and ready");

        imagePicker.sourceType =  UIImagePickerControllerSourceTypeCamera;
        imagePicker.delegate = self;
        imagePicker.allowsEditing = NO;
        imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;

        NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; for (AVCaptureDevice *device in devices) 
        {
            if([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2.0) 
            {
                imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;


            }
        }


        imagePicker.modalTransitionStyle = UIModalTransitionStyleCoverVertical;

        [self presentModalViewController:imagePicker animated:YES];

    }


    else
    {
        NSLog(@"Camera is not available");
        UIAlertView *cameraAlert = [[UIAlertView alloc] initWithTitle:@"Error" 
                                                              message:@"Your device doesn't seem to have a camera!" 
                                                             delegate:self cancelButtonTitle:@"Dismiss" 
                                                    otherButtonTitles:nil];
        [cameraAlert show];

    }
}
Run Code Online (Sandbox Code Playgroud)

编辑3:我登录viewDidUnload,它实际上不会被调用,所以我现在要求loadBackground中viewWillAppear,使我backgroundImageView无的viewDidDisappear.我希望这会有所帮助,但没有任何区别.

- (void)viewWillAppear:(BOOL)animated
{
    [self loadBackground];
}


- (void)viewDidDisappear:(BOOL)animated
{
    NSLog(@"ViewDidDisappear");
    backgroundImageView = nil;
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*uss 6

之间的关系UIImage,并UIImageView不一定直观的为大家.

UIImage 是图像数据的高级表示 - 仅此一点,它在显示数据方面没有任何作用.

UIImageView与工作UIImage,让您可以显示图像.

没有理由为什么多个实例UIImageView无法显示相同的内容UIImage.这是好的和有效的,因为只有一个内存中的图像数据表示,由多个视图共享.

您似乎正在做的是UIImage通过从磁盘加载每个视图来创建一个新视图.因此,这在两个方面是一个糟糕的通用设计:UIImage反复实例化实际上相同的内容,并重复从磁盘重新加载相同的图像数据.

您的内存问题实际上是一个单独的问题,您没有正确地释放您继续加载到UIImage对象中的图像数据UIImageViews.

从理论上讲,你应该能够UIImage获得第一个你从中得到的东西,UIImagePickerController并简单地将该引用传递给你的视图,而无需从磁盘重新加载.

如果您需要从磁盘保存和重新加载,因为更高级别的功能要求(例如,因为用户正在更改图像并且您希望保留它),您需要确保完全拆除以前的图像UIView,从它的视图层次结构中删除它.在dealloc方法中为视图设置断点以确认它被删除和解除分配是有帮助的,并确保设置iVar对子视图的任何引用(看起来你的backgroundImageView是iVar)都设置为nil.如果你没有正确拆除它backgroundImageView,它将继续保持对UIImage你设置为它的图像属性的引用.