【问题标题】:CALayer memory usage on draw - implicit cache?绘制时的CALayer内存使用情况 - 隐式缓存?
【发布时间】:2011-04-19 22:03:06
【问题描述】:

我在 iPhone 上遇到了 CoreGraphics/CoreAnimation 的特殊问题。为了更好地解释问题是如何表现出来的,我将引导您完成我当前的设置,并在适当的地方用代码进行说明。

我正在尝试在UIViewCALayer 中绘制一堆预加载的图像,但是每当图像显示时,应用程序的内存使用量就会激增,并且每当图像发生变化时,内存都不会被回收。

通过UIImage 的工具读取图像,将它们渲染到位图上下文并从该上下文中提取CGImageRef 来完成图像的预加载。这样做的目的是对图像进行解压缩和缩放,这样这些操作就不会在每次绘制时发生。在 Apple Q&A 中可以找到类似的建议(如果您好奇,请搜索 CGContextDrawImage performance)。上下文设置为每个组件 8 位和预乘 alpha。

图像解压缩为位图后,它们存储在NSArray 中,随后被分配(不保留)给执行绘图的自定义UIView 子类。我尝试了各种方法来实际绘制图像,到目前为止,最快的方法是直接设置视图的CALayercontents 属性。 drawLayer:inContext:drawRect: 等其他方法对帧速率的影响各不相同,但它们都表现出相同的内存行为。

问题是..contents 属性更改后,我看到 Instruments 中的内存出现峰值,即使图像不再显示,内存也不会下降。对象分配保持不变,所以我唯一的猜测是CoreAnimation 正在创建一些隐式缓存以加快绘图速度。然而,正如我所说,缓存并没有在应该释放的时候释放,并且逐渐积累会在运行几分钟后导致崩溃。

contents 属性保留了对象,我没有明确释放它,因为我希望原始图像在应用程序执行期间保留在内存中;最重要的是,高保留计数并不能解释我看到的内存峰值。

在检查堆栈时,我看到CoreAnimation 调用了诸如CA::Render::copy_image 之类的函数,这让我相信它正在将层的内容复制到某个遥不可及的地方。我想这是有正当理由的,但不知道如何以及何时清除它目前是一个令人震惊的错误。

如果我做错了什么,那么任何人都可以向我解释一下我是否做错了什么以及如何解决这种行为。

谢谢。

【问题讨论】:

  • “通过 UIImage 的工具读取它们”到底是什么意思?
  • imageWithContentsOfFile: 或 imageWithData:
  • 大家好,让我感到震惊的是,你们对 CALayer 的了解比我更好。我发布了一个 CALayer malloc 问题,如果你们中的任何一个可以看一下,我将非常感激...stackoverflow.com/questions/26715727/…

标签: objective-c cocoa-touch memory core-animation calayer


【解决方案1】:

@CouchDeveloper - 感谢您抽出宝贵时间回复。

我做了一些进一步的挖掘,我会回答我自己的问题,而不是直接回答,也许我获得的见解会帮助其他有类似问题的人。

您对分配与内存使用的观察是正确的。我正在查看的内存是内存监视器(活动监视器)仪器中应用程序使用的“真实”(常驻)内存。

我想我已经找到了这些神秘内存分配的原因。当CoreAnimation 遇到不是本机像素格式的图像时,它会将图像复制到自己的缓冲区中,这似乎是我所看到的用法。我相信正确的格式是 32 位、主机字节顺序、预乘 alpha 优先(位图信息标志 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)。

为了找出正在复制的 CGImageRef 实例并更广泛地了解框架的内部工作原理,您可以在设备上使用 Core Animation 工具,或者指定一些环境变量并从命令启动 iPhone 模拟器-线。可以在here 找到所有标志的列表。在我的例子中,最有用的组合是CA_COLOR_COPYCA_LOG_IMAGE_COPIES,它们会给复制的图像一个青色覆盖并将副本记录到stderr。

我仍然遇到一些奇怪的问题(“无数据指针”、非预乘 alpha 等),但我认为这主要是我创建或传递图像的方式的问题,但我认为我在快速解决所有内存问题。

希望这对其他想知道他们的内存和性能问题来自何处的人有所帮助!

【讨论】:

    【解决方案2】:

    我正在按照您的描述进行操作 - 没有检查泄漏。提供来源可能会有所帮助。

    然而,我确实检查的是,CA 分配的系统内存没有记录在仪器的“对象分配”中,而是分别记录在仪器的“内存监视器”中的仪器“活动监视器”中 - 即“可用物理内存”。当在屏幕外环境中绘制图像时,“可用物理内存”会减少,并且在显示该图像时也会减少。

    在释放图像之前,不会释放内存(包括用于显示的内存)(不过,如果知道如何显式释放该内存会很棒)。

    在你的情况下,如果你有一个 UIImages 数组作为缓存,当图像数组被解除分配并且没有对 UIImage 对象的进一步引用时,应该释放用于该图像的系统内存。

    除非您提供您的来源,否则我怀疑您的 CGImageRef 的保留计数不平衡 - 因此,它们正在泄漏。请注意,不应将 CGImageRef 直接放入 NSArray - 这些行为不像 Objective-C 对象(也称为免费桥接至 UIImage) - 您需要显式调用 CGImageRetain/CGImageRelease。

    【讨论】:

      【解决方案3】:

      您找到解决问题的方法了吗? 我有完全相同的问题。我正在使用 32 位 PNG 文件,我认为这就足够了。 我确实确保我没有使用 ImageNamed,因为这个以缓存东西而闻名。

      我使用 imageWithData 甚至确保我的 NSData initWithContentsOfFile 具有“nocache”NSRead 选项,以防止 FS 缓存,但这并没有改变任何东西。

      更新:我发现我的问题是什么。没有从其超级视图中删除视图(未显示),并且似乎在某处维护了一些缓存...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-09
        • 2015-10-11
        • 1970-01-01
        • 2013-02-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多