【问题标题】:Why does loading a gif with CGImageSource cause a memory leak?为什么用 CGImageSource 加载 gif 会导致内存泄漏?
【发布时间】:2016-05-28 00:03:34
【问题描述】:

我的项目中有一个 gif 文件。我正在尝试在我的UIImageView 中显示该 gif 文件。这是我的代码:

NSData* gifOriginalImageData = [NSData dataWithContentsOfURL:url];// url of the gif file
CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)gifOriginalImageData, NULL);

NSMutableArray *arr = [[NSMutableArray alloc] init];

NSInteger imagesCountInGifFile = CGImageSourceGetCount(src);
CGImageRef imagesOut[imagesCountInGifFile];
for (int i = 0; i < imagesCountInGifFile; ++i) {
    [arr addObject:[UIImage imageWithCGImage:CGImageSourceCreateImageAtIndex(src, i, NULL)]];
}

self.onboardingImageView.animationImages = arr;
self.onboardingImageView.animationDuration = 1.5;
self.onboardingImageView.animationRepeatCount = 2;
[self.onboardingImageView startAnimating];

我可以使用此代码成功显示 gif 文件。但是,它会导致内存泄漏高达 250mb,包含 60 张图像。我尝试使用给定的代码减少内存,但是再次没有成功。

self.onboardingImageView.animationImages = nil;
CFRelease(src);

任何帮助将不胜感激。 编辑:我添加了一个,这可能是一个泄漏。

【问题讨论】:

  • 单张图片的尺寸是多少?你不能在将它们添加到数组之前压缩这些图像吗?
  • 图片大小为100kb。我认为这并不过分。 @TejaNandamuri
  • 你也应该释放imagesOut,因为有"ZZZCreate`
  • 我不知道为什么你需要一个 imagesOut 的数组 - 只需为循环的每次迭代创建一个 CGImage,将其包装为 UIImage,然后将 UIImage 分配给你的数组并释放CGImage。一般规则是每次调用包含单词Create 的函数时,都应该有一个匹配的释放调用。
  • 释放 imagesOut 并没有解决问题@Larme。

标签: ios objective-c


【解决方案1】:

如果一个函数中有Create这个词,这表明你有责任释放它返回的东西的内存。在这种情况下,CGImageSourceCreateImageAtIndex 中的the documentation 明确表示:

返回一个 CGImage 对象。您负责使用 CGImageRelease 释放此对象。

您反复调用CGImageSourceCreateImageAtIndex,但从未真正释放它返回的CGImage,因此导致内存泄漏。一般的经验法则是每次调用包含单词 Create 的函数 - 你应该有一个等效的版本。

我也看不出拥有CGImages 数组的意义(因为您创建了UIImages 数组)。这意味着您所要做的就是在循环的每次迭代中创建您的CGImage,将其包装在UIImage 中,将此图像添加到您的数组中,然后最终释放CGImage。例如:

CGImageSourceRef src = CGImageSourceCreateWithData((__bridge CFDataRef)gifOriginalImageData, NULL);

NSMutableArray *arr = [[NSMutableArray alloc] init];

NSInteger imagesCountInGifFile = CGImageSourceGetCount(src);
for (int i = 0; i < imagesCountInGifFile; ++i) {
    CGImageRef c = CGImageSourceCreateImageAtIndex(src, i, NULL); // create the CGImage
    [arr addObject:[UIImage imageWithCGImage:c]]; // wrap that CGImage in a UIImage, then add to array
    CGImageRelease(c); // release the CGImage <- This part is what you're missing!
}

CFRelease(src); // release the original image source

请注意,您还应该桥接您的 gifOriginalImageData 而不是强制转换(我假设您使用的是 ARC)。

通过更改,上面的代码运行良好,没有任何内存泄漏。

【讨论】:

  • 抱歉这么晚才回复。然而泄漏并没有消失。
  • @ridvankucuk 你能用你当前的代码更新 OP 吗?尝试在仪器中分析应用程序是否存在泄漏,它应该会告诉您哪个函数调用导致了泄漏。假设您在添加到 OP 的屏幕截图中显示的代码是您当前的代码,它确实会泄漏,因为您没有从 CGImageSourceCreateImageAtIndex 释放 CGImage。您需要在 3 行中完成该操作(创建、包装和添加到数组,然后释放),就像我在上面演示的那样。如果您愿意,我很乐意澄清我的回答:)
  • @ridvankucuk:就像originaluser2 说的那样,您需要像他/她建议的那样在 3 行中完成该代码,而不是像您所做的那样在一行中完成。你需要CGImageRelease()CGImageSourceCreateImageAtIndex() 的结果来平衡。
【解决方案2】:

您需要使用 NSData 数组与 UIImage 数组。图像数据默认压缩,使用 UIImage 会解压缩数据。

此外,UIImage 在后台有一些缓存,可能会占用内存。

Michael Behan 就该主题写了一篇很棒的博文:http://mbehan.com/post/78399605333/uiimageview-animation-but-less-crashy

并且他编写了一个有用的库(MIT 许可证)来防止这种情况发生:https://github.com/mbehan/animation-view

【讨论】:

    猜你喜欢
    • 2013-07-12
    • 1970-01-01
    • 2017-02-13
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    相关资源
    最近更新 更多