绘图上的CALayer内存使用情况 - 隐式缓存?

时间:2010-10-01 15:05:40

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

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

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

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

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

问题是......在contents属性发生变化后,我发现Instruments中的内存出现峰值,即使图像不再显示,内存也不会下降。对象分配保持不变,所以我唯一的猜测是CoreAnimation正在创建一些隐式缓存来加速绘图。然而,正如我所说的那样,缓存并没有在它应该发布的情况下释放,并且渐渐的构建只会在运行几分钟后导致崩溃。

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

在检查堆栈时,我发现CoreAnimation调用了CA::Render::copy_image等函数,这让我相信它正在复制图层内容的某个地方。我想这是有正当理由的,但不知道如何以及何时清除它目前是一个停止显示的错误。

任何对CA有错综复杂知识的人都可以向我解释我是否做错了什么以及如何解决这个问题。

感谢。

3 个答案:

答案 0 :(得分:5)

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

我做了一些进一步的挖掘,我会回答我自己的问题,而不是直接回复,也许我所获得的洞察力将帮助其他人遇到类似的问题。

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

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

为了找出正在复制的CGImageRef实例并更深入地了解框架的内部工作,您可以在设备上使用Core Animation工具,或指定一些环境变量并从命令启动iPhone模拟器-线。可以找到所有标志的列表here。就我而言,最有用的组合是CA_COLOR_COPYCA_LOG_IMAGE_COPIES,这将为复制的图像提供青色叠加并将日志副本发送给stderr。

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

希望这可以帮助其他人想知道他们的记忆和性能问题来自何处!

答案 1 :(得分:2)

我正在做你所描述的 - 没有检查泄漏。也许在您提供来源时会有所帮助。

然而,我要检查的是,CA分配系统内存,这些内存未记录在仪器的“对象分配”中,而是分配在仪器的“活动监视器”中,分别位于仪器的“内存监视器”中 - 即“物理内存自由”。在屏幕外的环境中绘制图像时“物理内存自由”会减少,而在显示图像时也会减少。

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

在您的情况下,如果您有一个UIImages数组作为缓存,则在释放图像数组时以及没有对UIImage对象的进一步引用时,应释放用于此图像的系统内存。

除非你提供你的来源,否则我怀疑你的CGImageRefs的保留计数是不平衡的 - 因此,它们正在泄漏。请注意,CGImageRef不应该直接放在NSArray中 - 这些行为不像Objective-C对象(也就是免费桥接到UIImage) - 你需要明确地调用CGImageRetain / CGImageRelease。

答案 2 :(得分:1)

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

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

更新:我发现了我的问题。没有从超级视图中删除视图(没有显示),而且似乎在某处保留了一些缓存......