使用UIImagePicker进行内存故障

时间:2010-05-27 13:35:46

标签: iphone cocoa-touch memory-management uiimagepickercontroller

我正在构建一个具有多个不同部分的应用程序,所有部分都非常重要。它与我的客户的网站联系在一起,他们是一个“高设计”类型的服装。

应用程序的一部分是从相机或库上传的图像,以及显示缩略图网格的tableview。非常可靠,当我处理相机版本的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的相机光圈关闭的时候。我点击按钮拍照,它做“点击”动画,仪器显示我的内存占用量从大约10mb到大约25mb,并坐在那里直到图像被传送到我的UIViewController,其中使用率回落到10或11mb再次。如果我们在没有记忆警告的情况下通过它,我们就是金色的,但我们很可能不会。我能做些什么才能让它变得那么贵?

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

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

1 个答案:

答案 0 :(得分:4)

这些功能可让您了解总内存使用量和总可用内存。我碰巧在我的应用程序中有一个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);
    }
}

致谢:来自herehere的无记忆/已使用功能。