UIImagePicker 的内存问题

Posted

技术标签:

【中文标题】UIImagePicker 的内存问题【英文标题】:Memory Troubles with UIImagePicker 【发布时间】:2011-02-24 16:19:24 【问题描述】:

我正在构建一个包含几个不同部分的应用程序,所有这些部分都非常注重图像。它与我客户的网站相关联,它们是“高设计”类型的服装。

应用程序的一部分是从相机或库上传的图像,以及显示缩略图网格的表格视图。相当可靠,当我处理 UIImagePickerControl 的相机版本时,我会因为内存不足而受到打击。如果我在应用程序的该部分反弹一段时间,我偶尔会在调试器中出现“status:10 (SIGBUS)”且不可重复地崩溃。

在内存不足警告时,我的应用程序这方面的根视图控制器转到我的数据管理单例,浏览缓存数据数组,并杀死最大的部分,即与每个条目关联的图像。因此:

- (void)didReceiveMemoryWarning 
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Low Memory Warning"
                                                    message:@"Cleaning out events data"
                                                   delegate:nil
                                          cancelButtonTitle:@"All right then."
                                          otherButtonTitles:nil];
    [alert show];
    [alert release];

    NSInteger spaceSaved;

    DataManager *data = [DataManager sharedDataManager];
    for (Event *event in data.eventList) 
        spaceSaved += [(NSData *)UIImagePNGRepresentation(event.image) length];
        event.image = nil;
        spaceSaved -= [(NSData *)UIImagePNGRepresentation(event.image) length];
    

    NSString *titleString = [NSString stringWithFormat:@"Saved %d on event images", spaceSaved];

    for (WondrMark *mark in data.wondrMarks) 
        spaceSaved += [(NSData *)UIImagePNGRepresentation(mark.image) length];
        mark.image = nil;
        spaceSaved -= [(NSData *)UIImagePNGRepresentation(mark.image) length];
    

    NSString *messageString = [NSString stringWithFormat:@"And total %d on event and mark images", spaceSaved];

    NSLog(@"%@ - %@", titleString, messageString);

    // Relinquish ownership any cached data, images, etc that aren't in use.

如您所见,我正在(糟糕的)尝试观察我正在释放的内存空间。我知道它并没有告诉我 UIImages 本身的实际内存占用,但它至少给了我一些数字,所以我可以看到正在发生的事情。 (对于我构建 NSLog 消息的笨拙方式感到抱歉——我打算触发另一个 UIAlertView,但意识到记录它会更有用。)

相当可靠,在应用程序的图像部分处理了一段时间后,我将拉起相机界面并快速连续三四次获得低内存 UIAlertView。这是我上次看到的 NSLog 输出:

2010-05-27 08:55:02.659 EverWondr[7974:207] Saved 109591 on event images - And total 1419756 on event and mark images
wait_fences: failed to receive reply: 10004003
2010-05-27 08:55:08.759 EverWondr[7974:207] Saved 4 on event images - And total 392695 on event and mark images
2010-05-27 08:55:14.865 EverWondr[7974:207] Saved 4 on event images - And total 873419 on event and mark images
2010-05-27 08:55:14.969 EverWondr[7974:207] Saved 4 on event images - And total 4 on event and mark images
2010-05-27 08:55:15.064 EverWondr[7974:207] Saved 4 on event images - And total 4 on event and mark images

然后很快我们就得到了 SIGBUS 出口。所以情况就是这样。现在我的具体问题:

我看到这种情况发生的时间是 UIPickerView 的相机光圈关闭时。我单击按钮拍照,它执行“单击”动画,Instruments 显示我的内存占用量从大约 10mb 到大约 25mb,并坐在那里,直到图像传送到我的 UIViewController,使用量回落到 10 或又是 11mb。如果我们在没有记忆警告的情况下通过它,我们就是黄金,但很可能我们没有。我能做些什么来让它不那么贵?

其次,我启用了 NSZombies。我是否正确理解这实际上是在阻止内存被释放?我是否将我的应用置于不公平的测试环境中?

第三,有没有办法以编程方式获取我的内存使用情况?或者至少是 UIImage 对象的用法?我已经搜索了文档,但没有看到任何相关内容。

【问题讨论】:

我认为当您收到内存警告时调用 UIImagePNGRepresentation 是个坏主意,它将分配 更多 内存。 是的,这当然不是生产代码方法。在试图弄清楚发生了什么事情时,有点像冰雹玛丽传球。 【参考方案1】:

这些函数让您了解您的总内存使用量和总可用内存。我的应用中碰巧有一个 1 秒计时器,它每秒记录已使用和可用内存空间(如果更改 > 0.5mb),它可以帮助我更好地了解正在发生的事情:

#import "mach/mach.h"

vm_size_t usedMemory(void) 
    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
    return (kerr == KERN_SUCCESS) ? info.resident_size : 0;   // size in bytes


natural_t freeMemory(void) 
    mach_port_t           host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t              pagesize;
    vm_statistics_data_t   vm_stat;

    host_page_size(host_port, &pagesize);
    (void) host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size); 
    return vm_stat.free_count * pagesize;

EDIT1 - 函数class_getInstanceSize 为您提供对象实例的大小,但我从未尝试过它,它可能不会取消引用 ivars 并汇总它们的使用情况。但也许它可以帮助你。

EDIT2此函数为您提供 UIImage 的大小:

size_t sizeofUIImage(UIImage* image) 
    return CGImageGetBytesPerRow(image.CGImage) * CGImageGetHeight(image.CGImage);

EDIT4 这是我的 1 秒计时器处理程序方法中的一些代码,它有助于在出现内存问题之前了解您的应用程序的运行情况。当然,可用内存取决于正在运行的后台任务(Safari、Mail 等)而您无法控制:

void logMemUsage(void) 
    // compute memory usage and log if different by >= 100k
    static long prevMemUsage = 0;
    long curMemUsage = usedMemory();
    long memUsageDiff = curMemUsage - prevMemUsage;

    if (memUsageDiff > 100000 || memUsageDiff < -100000) 
        prevMemUsage = curMemUsage;
        NSLog(@"Memory used %7.1f (%+5.0f), free %7.1f kb", 
            curMemUsage/1000.0f, memUsageDiff/1000.0f, freeMemory()/1000.0f);
    

致谢:来自here 和here 的内存空闲/使用函数。

【讨论】:

我的 C 很差(而我的 Obj-C 只是稍微好一点)。我将它粘贴到我的视图控制器中,并且在每一行都出现了编译错误。有什么我需要包含的吗? 对不起,你需要#import "mach/mach.h" 我还添加了一个 freeMemory() 函数。见上文。 谢谢。什么是“size_t”?我可以把它当作一个 int 对待吗? 是的,size_t 通常是 unsigned long 或类似的。上面的 Edit4 显示了我如何在我的 1 秒计时器方法中记录这些信息,只是为了给你一个想法。我希望这对您的第三个问题有所帮助。

以上是关于UIImagePicker 的内存问题的主要内容,如果未能解决你的问题,请参考以下文章

iPhone 中 UIImagePickerController 的内存泄漏问题

当图像保存在核心数据的集合视图中时,应用程序因内存错误而崩溃

UIIMagePicker 相机横向方向问题

测试 UIImagePicker

更改 UIImagePicker 纵横比

uiimagepicker 用于显示相机