更新UIImageView需要太多内存

时间:2013-10-24 08:53:25

标签: ios objective-c uiimageview core-graphics grand-central-dispatch

我有一个大约每20ms执行一次的代码块(它是Core Audio音频单元的渲染回调方法);这种方法运行一些计算并更新UIImageView的图像。 为了强制刷新UI,我在主线程中安排了UIImageView图像的setter,如下所示:

[audioUnit setOutputBlock:^() {
    // ...
    // some heavy calculations
    // ...
    dispatch_sync(dispatch_get_main_queue(), ^{    
        imageView.image = images[index];
    });        
}];

没有图像的设置者,应用程序需要大约50mb的内存,而图像的刷新使其跳转到大约300mb,导致iPhone上的内存警告和崩溃< 5。

面对这样的问题最好的方法是什么?

非常感谢, DAN

1 个答案:

答案 0 :(得分:7)

这里的数据集相当大。在解压缩图像后考虑它:1136高度x 640宽度x每个像素4个字节x 110个图像= 320MB。不幸的是,图像解压缩不是快速/自由的,所以如果你想要在50Hz发生这种情况,那么你将不得不玩一些技巧。首先,您可能无法同时将所有110张图像保存在内存中,因此请不要尝试这样做。

另外,请勿使用-[UIImage imageNamed:]。它以您无法控制的方式积极地缓存图像的解压缩版本。我会使用[[UIImage alloc] initWithContentsOfFile:]来最大程度地控制图像的生命周期。

如果解压缩时间是可以容忍的,那么它可能就像保持图像的路径数组一样简单,然后在最后一分钟制作一个UIImage对象。我怀疑每次减压20ms会有点太慢。

另一种选择是预解压缩图像,然后将解压缩的数据写入磁盘上的应用程序缓存目录。这样,您仍然可以从磁盘加载,但它是直接读取而不是每次都是读取和解压缩。请注意,这必然会消耗〜320MB的磁盘存储空间。 太可怕了。

为了更进一步,使用一些额外的恶作剧,您可能能够获得它以便图像在磁盘上,然后应用程序中的图像对象可以指向与每个文件对应的mmap的内存区域在磁盘上。通过这种方式,您可以让虚拟内存系统决定在内存中有什么重要,哪些内存不重要,您可以“免费”获得。这似乎是最好的拍摄。 (编辑:我看了,似乎UIImage已经在幕后制作了图像,所以这个建议可能对你没什么帮助。)

我从你发布的片段中注意到一件事:你正在块闭包中捕获整个数组,这意味着数组中的每个图像在该块的持续时间内保持活动状态(并且一段时间更长,直到libdispatch决定释放该块。)它可能不会足够来避免这种情况(因此我的建议如上)但它肯定无助于捕获整个数组。这就是我的意思:

[audioUnit setOutputBlock:^() {
    @autoreleasepool {

        NSArray* images = [NSArray array]; // or whatever
        // ...
        // some heavy calculations
        // ...
        UIImage* theOneImageWeNeed = images[index];
    }
    dispatch_sync(dispatch_get_main_queue(), ^{    
        imageView.image = theOneImageWeNeed;
    });        
}];

这将保留您需要的一个图像,并更积极地释放计算生成的任何其他对象。