使用UIImagePickerController减少内存使用量

 sprout--_557 发布于 2023-02-07 11:19

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

我在内存管理方面遇到了一些麻烦.随着今天手机上的摄像头以百万像素迅速上升,从UIImagePickerController返回的UIImages是记忆猪.在我的iPhone 4S上,UIImages约为5MB; 我很难想象它们在新的和未来的模型上会是什么样的.

我的一位朋友说,处理UIImages的最佳方法是立即将它们保存到我应用程序文档目录中的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应该从内存中释放出来,对吧?

我该怎么做才能减少应用的内存使用量?

更新

我现在意识到分配探查器的屏幕截图有助于解释这个问题.

app的分配

1 个回答
  • 你的processImage方法不是你的问题.

    我们可以通过将其移植到Apple的PhotoPicker演示应用程序来测试您的图像保存代码

    方便的是,Apple的示例项目与您的非常相似,有一种方法可以在计时器上重复拍摄照片.在示例中,图像不会保存到文件系统,而是累积在内存中.它伴随着这个警告:

    /* Start the timer to take a photo every 1.5 seconds.
    CAUTION: for the purpose of this sample, we will continue to take pictures indefinitely.
    Be aware we will run out of memory quickly. You must decide the proper threshold number of photos allowed to take from the camera.
    One solution to avoid memory constraints is to save each taken photo to disk rather than keeping all of them in memory.
    In low memory situations sometimes our "didReceiveMemoryWarning" method will be called in which case we can recover some memory and keep the app running.
    */

    将您的方法添加到Apple的代码中,我们可以解决此问题.

    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) - 对于我们的测试,我们使用cameraTimer拍摄重复图像,因此finishAndUpdate不会被调用.

    processImage:按原样使用了你的方法,使用了:
    [self uploadImage:imageData withFilename:filename];
    注释掉了.

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

    static int imageName = 0;
    
    -(NSString*)makeImageFilename {
        imageName++;
        return [NSString stringWithFormat:@"%d.jpg",imageName];
    }
    

    这些是我对Apple代码的唯一补充.

    这是Apple原始代码的内存占用(cameraTimer不带(1)和(2))

    在此输入图像描述

    在拍摄了约40张图像后,内存攀升至约140MB

    这是添加的内存占用(cameraTimer与(1)和(2)一起运行)

    在此输入图像描述

    文件存储方法正在解决内存问题:内存是平坦的,每个图像捕获的峰值大约为30MB.

    这些测试是在iPhone5S上运行的.未压缩的图像为3264 x 2448 px,应为24mB(24位RGB).Jpeg压缩(文件系统)大小范围在250kB(0.1质量,根据您的代码)到1-2mB(0.7质量)高达~6mB(1.0质量).

    在对您的问题的评论中,您建议重新加载的图像将受益于该压缩.情况并非如此:当图像加载到内存中时,必须先将其解压缩.它的内存占用量大约等于像素x颜色x每个颜色的位深度 - 无论图像存储在磁盘上的方式如何.正如jrturton指出的那样,这至少表明你应该避免以比你需要的显示更高的分辨率加载图像.假设您有一个832 x 640的全屏(视网膜)imageView,如果您的用户无法放大,则会浪费内存加载大于该值的图像.这是一个约1.6mB的实时内存占用,对您的24mMB原始内容有很大的改进(但这是你主要问题的题外话).

    由于processImage似乎不是导致记忆困难的原因,您应该考虑其他可能性:

    1 /您没有内存问题.你是如何分析应用程序的?
    2 /其中一个addImageuploadImage正在保留记忆.尝试依次评论每个,以确定哪个.
    3 /问题出在其他地方(由PhoneGap管理的事情?)

    至于那些存储器尖峰,这些由图像-数据的JPEG压缩线引起的:
    NSData* imageData = UIImageJPEGRepresentation(image, 0.1);

    在幕后,即ImageIO,使用ImagePickerController时可能无法避免.看到这里: 大多数内存有效的方式将照片保存到iPhone上的磁盘? 如果您切换到AVFoundation,您可以将图像作为未转换的NSData获取,这样您就可以避免尖峰.

    2023-02-07 11:22 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有