简体   繁体   中英

Why does loading a gif with CGImageSource cause a memory leak?

I have a gif file in my project. I am trying to display that gif file in my UIImageView . Here is my code:

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];

I can display the gif file successfully with this code. However it causes a memory leak up to 250mb with 60 images. I have tried to reduce the memory with the given code however there is no success again.

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

Any help would be appreciated. Edit: I have added an 图片 which is a potentially leak.

If a function has the word Create in it, that's an indication that you're responsible for releasing the memory of the thing that it returns. In this case, the documentation of CGImageSourceCreateImageAtIndex explicitly says:

Returns a CGImage object. You are responsible for releasing this object using CGImageRelease.

You are repeatedly calling CGImageSourceCreateImageAtIndex , but never actually releasing the CGImage that it returns, therefore resulting in a memory leak. The general rule of thumb is for every call to a function that has the word Create in it – you should have an equivalent release.

I also don't see the point in having an array of your CGImages (as you create an array of UIImages ). This means that all you have to do is create your CGImage at each iteration of the loop, wrap it in a UIImage , add this image to your array and then finally release the CGImage . For example:

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

Note that you should also be bridging your gifOriginalImageData instead of casting (I assume you're using ARC).

With the changes, the above code runs fine without any memory leaks.

You'll want to work with an array of NSData vs. an array of UIImage. Image data is compressed by default and working with a UIImage decompresses the data.

Also, UIImage has some caching under the hood that can be hard on memory.

Michael Behan wrote a great blog post on the topic: http://mbehan.com/post/78399605333/uiimageview-animation-but-less-crashy

And he wrote a helpful library (MIT License) to prevent this from happening: https://github.com/mbehan/animation-view

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM