Ios 使用UIImagePickerController减少内存使用

Ios 使用UIImagePickerController减少内存使用,ios,memory-management,uiimage,automatic-ref-counting,uiimagepickercontroller,Ios,Memory Management,Uiimage,Automatic Ref Counting,Uiimagepickercontroller,在我的应用程序中,用户可以使用UIImagePickerController拍摄多幅图像,然后在视图中逐个显示这些图像 我在内存管理方面遇到了一些问题。随着如今手机上的摄像头以百万像素的速度快速增长,从UIImagePickerController返回的UIImages会占用大量内存。在我的iPhone4S上,UIImage大约为5MB;我很难想象新的和未来的车型会是什么样子 我的一位朋友说,处理UIImage的最佳方法是立即将它们保存到我应用程序文档目录中的JPEG文件中,并尽快发布原始UII

在我的应用程序中,用户可以使用UIImagePickerController拍摄多幅图像,然后在视图中逐个显示这些图像

我在内存管理方面遇到了一些问题。随着如今手机上的摄像头以百万像素的速度快速增长,从UIImagePickerController返回的UIImages会占用大量内存。在我的iPhone4S上,UIImage大约为5MB;我很难想象新的和未来的车型会是什么样子

我的一位朋友说,处理UIImage的最佳方法是立即将它们保存到我应用程序文档目录中的JPEG文件中,并尽快发布原始UIImage。这就是我一直想做的。不幸的是,即使将UIImage保存为JPEG格式并在代码中不留任何引用,它也不会被垃圾收集

以下是我的代码的相关部分。我正在使用ARC

// Entry point: UIImagePickerController delegate method
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

    // Process the image.  The method returns a pathname.
    NSString* path = [self processImage:[info objectForKey:UIImagePickerControllerOriginalImage]];

    // Add the image to the view
    [self addImage:path];
}

-(NSString*) processImage:(UIImage*)image {

    // Get a file path
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* documentsDirectory = [paths objectAtIndex:0];
    NSString* filename = [self makeImageFilename]; // implementation omitted
    NSString* imagePath = [documentsDirectory stringByAppendingPathComponent:filename];

    // Get the image data (blocking; around 1 second)
    NSData* imageData = UIImageJPEGRepresentation(image, 0.1);

    // Write the data to a file
    [imageData writeToFile:imagePath atomically:YES];

    // Upload the image (non-blocking)
    [self uploadImage:imageData withFilename:filename];

    return imagePath;
}

-(void) uploadImage:(NSData*)imageData withFilename:(NSString*)filename {
    // this sends the upload job (implementation omitted) to a thread
    // pool, which in this case is managed by PhoneGap
    [self.commandDelegate runInBackground:^{
        [self doUploadImage:imageData withFilename:filename];
    }];
}

-(void) addImage:(NSString*)path {
    // implementation omitted: make a UIImageView (set bounds, etc).  Save it
    // in the variable iv.

    iv.image = [UIImage imageWithContentsOfFile:path];
    [iv setNeedsDisplay];
    NSLog(@"Displaying image named %@", path);
    self.imageCount++;
}
请注意,
processImage
方法如何引用UIImage,但它只将其用于一件事:制作该图像的NSData*表示。所以,在processImage方法完成后,UIImage应该从内存中释放,对吗

如何减少应用程序的内存使用

更新 我现在意识到分配分析器的屏幕截图将有助于解释这个问题


您的
processImage
方法不是您的问题

我们可以将您的图像保存代码移植到

方便的是,苹果的示例项目与您的非常相似,有一种在计时器上重复拍照的方法。在本示例中,映像不会保存到文件系统中,而是累积到内存中。它附带以下警告:

/*
启动计时器,每1.5秒拍摄一张照片。

注意:对于本示例,我们将继续无限期拍照。
请注意,我们将很快耗尽内存。您必须确定允许从相机拍摄的照片的适当阈值数量。
避免内存限制的一个解决方案是将每张照片保存到磁盘,而不是将所有照片都保存在内存中。
在内存不足的情况下,有时会调用“didReceiveMemoryWarning”方法,在这种情况下,我们可以恢复一些内存并保持应用程序运行。
*/

通过将您的方法添加到苹果的代码中,我们可以解决这个问题

imagePicker委托方法如下所示:

- (void)imagePickerController:(UIImagePickerController *)picker 
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage];

    [self.capturedImages removeAllObjects];   // (1)
    [self.imagePaths addObject:[self processImage:image]]; //(2)

    [self.capturedImages addObject:image];

    if ([self.cameraTimer isValid])
    {
        return;
    }
    [self finishAndUpdate]; //(3)
}
(1) -我们的新增功能是刷新每个图像捕获事件的实时内存
(2) -我们的新增功能是将映像保存到文件系统并构建文件系统路径列表。
(3) -在我们的测试中,我们使用摄像机拍摄重复图像,因此不会调用
finishAndUpdate

我已经按原样使用了您的
processImage:
方法,其中的行是:
[自上传图像:带文件名的图像数据:文件名]
评论说

我还添加了一个小的makeImageFileName方法:

static int imageName = 0;

-(NSString*)makeImageFilename {
    imageName++;
    return [NSString stringWithFormat:@"%d.jpg",imageName];
}
这些是我对苹果代码的唯一补充

这是苹果原始代码的内存占用(cameraTimer运行时没有(1)和(2))

在捕获约40幅图像后,内存攀升至约140MB

以下是添加的内存占用(cameraTimer使用(1)和(2)运行)

文件保存方法解决了内存问题:内存是平坦的,每次图像捕获的峰值约为30MB

这些测试是在iPhone5S上运行的。未压缩的图像是3264x2448px,大约24mB(24位RGB)。Jpeg压缩(文件系统)的大小范围在250kB(根据您的代码,质量为0.1)到1-2mB(质量为0.7)到6mB(质量为1.0)之间

在对您的问题的评论中,您建议重新加载的图像将受益于该压缩。事实并非如此:当图像加载到内存中时,必须首先对其进行解压缩。它的内存占用大约等于像素x颜色x每种颜色的位深度-无论图像存储在磁盘上的方式如何。正如已经指出的,这至少表明您应该避免加载分辨率高于显示所需的图像。假设您有一个832 x 640的全屏(视网膜)图像视图,如果您的用户无法放大,那么加载一个比这个更大的图像是在浪费内存。这是一个约1.6mB的实时内存占用,比原来的24mMB有了巨大的改进(但这与您的主要问题无关)

由于
processImage
似乎不是造成记忆问题的原因,您应该考虑其他可能性:

1/您没有内存问题。您如何分析应用程序?
2/其中一个
addImage
uploadImage
正在保留内存。试着依次对每一个进行注释,以确定是哪一个。
3/问题在别处(由PhoneGap管理的东西?)

关于这些内存峰值,它们是由图像到数据jpeg压缩行引起的:
NSData*imageData=uiimagejpeg表示法(图像,0.1)


引擎盖下是ImageIO,使用ImagePickerController时可能不可避免。请看这里:如果您切换到AVFoundation,您可以将图像作为未转换的NSData获取,这样您就可以避免出现峰值

UIImageView
需要多大的图像?根据上述代码,图像保存为jpeg(节省磁盘空间),然后在
addImage
方法中重新加载。当它再次加载时,图像的大小与保存为jpeg之前的大小相同。如果可能的话,在将其保存到磁盘之前,请将其重新缩放到适当的大小。确实,我正在为UIImageView重新加载图像,但JPEG表示仅为原始图像大小的10-15%,因此我应该仍然会看到内存的减少,对吗?据Instruments称,内存使用量在上升后不会下降。@vote539-尽管压缩的JPEG大小只有10-15%