【发布时间】:2016-01-28 21:53:04
【问题描述】:
为了简单起见,我使用下一个简化的代码生成位图:
for (int frameIndex = 0; frameIndex < 90; frameIndex++) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(130, 130));
// Making some rendering on the context.
// Save the current snapshot from the context.
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
[self.snapshots addObject:snapshot];
UIGraphicsEndImageContext();
}
所以没有什么不重要的,但是当操作系统为所有内容提供大约 30 MB 的内存时,一切都变得复杂了(在这种特殊情况下,它是 watch OS 2,但它不是依赖于操作系统的问题)并且超出配额,操作系统只是杀死应用程序的进程。
分配工具的下一张图表说明了这个问题:
这是同一张图,但内存消耗的注释不同 - 在上述代码执行之前、此时和之后。可以看出,最终生成了大约 5.7 MB 的位图,这是绝对可以接受的结果。不能接受的是图表顶部的内存消耗 (44.6 MB) - 所有这些内存都被CoreUI: image data 吃掉了。鉴于操作发生在后台线程中,执行时间并不那么重要。
所以问题是:减少内存消耗(可能通过增加执行时间)以适应内存配额的正确方法是什么?为什么尽管调用了UIGraphicsEndImageContext,但内存消耗却增加了?
更新 1: 我认为使用 NSOperation、NSTimer 等拆分整个操作可以解决问题,但仍会尝试提出一些同步解决方案。
试图收集所有答案并测试下一段代码:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(130, 130));
for (int frameIndex = 0; frameIndex < 45; frameIndex++) {
// Making some rendering on the context.
@autoreleasepool {
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
[self.snapshots addObject:snapshot];
}
CGContextClearRect(UIGraphicsGetCurrentContext(), CGSizeMake(130, 130));
}
for (int frameIndex = 0; frameIndex < 45; frameIndex++) {
// Making some rendering on the context.
@autoreleasepool {
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
[self.snapshots addObject:snapshot];
}
CGContextClearRect(UIGraphicsGetCurrentContext(), CGSizeMake(130, 130));
}
UIGraphicsEndImageContext();
发生了什么变化:
- 将 90 次迭代分成 45 次的 2 部分。
- 将图形上下文移到外部并在每次迭代后将其清除,而不是创建新上下文。
- 在自动释放池中打包并存储快照。
结果 - 没有任何改变,内存消耗保持在同一水平。
此外,如果完全取消拍摄和存储快照,它只会减少 4 MB 的内存消耗,即少于 10%。
更新 2:
每 3 秒通过计时器进行渲染生成下一张图:
正如您所见,即使渲染除以时间间隔,内存也没有被释放(准确地说 - 没有完全释放)。有些东西告诉我,在执行渲染的对象存在之前,内存不会被释放。
更新 3:
结合3种方法解决了这个问题:
- 将整个渲染任务拆分为子任务。例如,将 90 张图纸分成 6 个子任务,每个子任务 15 张图纸(15 张的数量是凭经验找到的)。
- 使用
dispatch_after以较小的间隔连续执行所有子任务(在我的情况下为 0.05 秒)。 -
最后也是最重要的。为了避免像上一张图那样的内存泄漏 - 每个子任务都应该在新对象的上下文中执行。例如:
self.snapshots = [[SnaphotRender new] renderSnapshotsInRange:[0, 15]];
感谢大家的回答,但@EmilioPelaez 最接近正确答案。
【问题讨论】:
-
为什么不创建一个计时器,它每秒调用一次生成图像的方法?
-
减少内存消耗的最简单方法是减小从上下文中捕获的生成图像(self.contextSize)的大小
-
图像的总字节大小可能是 130 * 130 * 4(每像素字节数)* 150 = ~10MB。不确定手表是否存在,但可能会建立临时内存,您可以尝试将
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();[self.snapshots addObject:snapshot];包装在@autoreleasepool块中。 -
... 另外,为什么不将
UIGraphicsBeginImageContextWithOptions和UIGraphicsEndImageContext移到循环之外呢?清除上下文并不难,无论您是否建立自己的临时池,您都绝对不会有 150 倍的处理和自动释放成本。 -
您使用的是
UIGraphicsBeginImageContextWithOptions还是UIGraphicsBeginImageContext?在第一种情况下,您使用的是哪个比例因子?
标签: objective-c memory