2

我有一个大约每 20 毫秒执行一次的代码块(它是 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 上崩溃。

面对此类问题的最佳方法应该是什么?

非常感谢,丹

4

1 回答 1

7

您的数据集在这里相当大。图像解压后想想:1136 高 x 640 宽 x 4 字节/像素 x 110 图像 = 320MB。不幸的是,图像解压不是快速/免费的,所以如果你想让它在 50Hz 下发生,你将不得不玩一些技巧。对于初学者,您可能无法同时将所有 110 张图像保存在内存中,因此请停止尝试。

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

如果解压时间是可以容忍的,那么它可能很简单,只需保留一组图像路径,然后UIImage在最后一分钟制作您需要的一个对象。我怀疑 20 毫秒会有点太慢,无法每次进行解压。

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

为了更进一步,通过一些额外的恶作剧,您可能能够获得它以便图像在磁盘上,然后应用程序中的图像对象可以指向与磁盘上每个文件对应的映射内存区域。通过这种方式,您可以让虚拟内存系统决定在内存中哪些是重要的,哪些不是,并且您可以“免费”获得。这似乎是最好的选择。(编辑:我看了,似乎 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;
    });        
}];

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

于 2013-10-24T11:57:39.543 回答