【问题标题】:Memory managment when drawing to a view on the iPhone在 iPhone 上绘制到视图时的内存管理
【发布时间】:2009-08-18 13:37:13
【问题描述】:

我有一个 UIImage,我正在使用 imageWithData: 实例化它:(数据是使用 [NSData dataWithContentsOfFile:] 从包中加载的)。

然后我像这样绘制图像:

NSData *imageData =  [NSData dataWithContentsOfFile:fileLocation];
UIImage *myImage = [UIImage imageWithData:imageData];

//These lines are superfluous from what I can tell, replacing with
//UIImage *myImage = [UIImage imagedNamed:imageName]; very soon.

[myImage drawAtPoint:CGPointMake(0,0)];
//myImage will be released at the end of the run loop

我的问题是:创建的 UIImage 是自动发布的。当 UIImage 被绘制到视图然后 UIImage 被解除分配不存在时,在内存方面会发生什么。显然,在视觉上,图像仍然存在,因为它已被绘制到上下文中。

如果 UIImage 有效且已被绘制到视图,内存使用量是否会翻倍,然后在 UIImage 被释放后返回到与仅存在一个 UIImage 相同的数量?


现在走另一条路。

如果我使用 [UIImage imageNamed:] 来实例化图像,那么 UIImage 类有自己的各种图像缓存,并且只会保存特定图像的一个真实实例(无论创建了多少 UIImage 实例来表示那一张图片)。

我的另一个问题是:如果我将缓存中的图像绘制到上下文然后释放 UIimage(通过运行循环结束时的自动释放),会发生什么情况?图像是否留在缓存中消耗内存?是否因为没有其他 UIImage 实例在使用它而被移除?

任何帮助都会很棒,谢谢!

【问题讨论】:

  • 能否提供更详尽的代码?
  • 老实说,没有更多的代码可以提供。不过,我会添加不存在的内容。

标签: iphone memory-management caching uikit uiimage


【解决方案1】:

我真的建议从这里开始使用简单的代码,然后优化您实际遇到问题的地方。您描述的那种空间优化可能会产生严重的时间问题(例如,在-drawRect: 中重新创建 UIImage 非常昂贵)。也就是说,让我们来看看各种问题:

  • 首先,正如我所说,在-drawRect: 做昂贵的工作要小心。您几乎无法控制调用它的频率或时间,并且任何昂贵的工作(例如创建新的 UIImage,特别是如果您必须从磁盘读取它)都会严重影响 UI 性能。

  • 我假设您的-drawRect: 有更多内容,对吗? UIImageView 针对您在此处所做的工作进行了优化,包括速度和内存。但如果你的视图复杂得多,那么自己绘制图像比创建大量子视图要好。

  • 如前所述,当您调用-drawAtPoint: 时,您制作的副本是位图表示(与已完成的任何其他绘图混合)。就内存使用而言,它与原始 UIImage 无关。你不能用一个换另一个。位图表示所需的内存是视图大小和位深度的函数,您无法更改它。它不关心 UIImage 绘制后是否存在。

  • -imageNamed: 确实为您做缓存,通常是一个不错的选择。请注意,它不会在内存不足的情况下清除其缓存。如果 UIImage 是从文件中加载的,它们本身会透明地转储其基础数据(并且在任何情况下它们都会转储额外的表示)。 UIImage 参考包含有关如何完成此操作的信息。

  • 如果您非常关心这些图像(空间或时间)的性能,并且不需要 UIImage 的功能,则应该使用 CGImages。它们不像 UIImage 那样灵活,而且它们的代码更复杂,但它们更高效。也就是说,UIImage 适用于大多数用途。

【讨论】:

  • UIImageView 没有优化,只是为了方便。 UILabel 也是如此。
  • 它声称它是(参见子类化注释)。它绕过了drawRect:。还没有深入研究它的作用,但他们声称正在做一些花哨的事情。
【解决方案2】:

在任何时候,当您“绘制到上下文”时,上下文不包含对图像的引用。相反,绘图会将实际的像素位放置在上下文中,因此它不需要对 UIImage 的引用(或任何绘制到它的东西 - NSString、NSPath 等)。

关于imageNamed:,它永远不会真正发布 - 你得到的是一个你不(也不应该)发布的参考,但缓存的图像可能仍然存在。

【讨论】:

  • 我自己不会发布它。 [UIImage imageNamed:] 返回一个自动释放的 UIImage 实例。它会在运行循环结束时释放自己。我想知道的是,缓存的图像是否会在释放引用它的任何 UIImages 时被释放?
  • 规范没有说明它是否释放缓存的图像。我会假设它没有。它可能在didReceiveMemoryWarning 的系统等效项中这样做,但同样:据我所知没有指定。
  • imageNamed:图像被缓存直到系统等效于 didReceiveMemoryWarning,但仅在 3.0--在 2.x 上,存在一个永远不会刷新缓存的错误。
【解决方案3】:

这实际上取决于您在哪里创建 UIImage 实例本身。如果这是 UIView 子类,您是在 -drawRect:-initWithFrame: 或其他地方创建 UIImage 吗?如果您加载一次图像(在您的 init 中或使用保留的 setter 等),那么应该没有问题。但是,如果您在 -drawRect: 中一遍又一遍地创建图像,那么至少您会遇到性能问题。

【讨论】:

  • 我在这里遇到的主要问题是,我不想保留在 drawRect 之前不会使用的图像。视图不会经常重绘自己,所以我认为在 drawRect 中实例化图像并不是什么大问题。如果我在 init 中实例化它,那么图像会消耗内存,即使它没有被使用——这对我来说没有意义。
  • 那么我会选择[UIImage imageNamed:],因为正如你所说,它会缓存图像,但如果内存紧张,可能会刷新缓存。这样您仍然可以获得参考,但实际数据本身可能会被刷新。 UIImage 负责所有繁重的工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-10
  • 2011-10-10
  • 2010-10-28
相关资源
最近更新 更多