Memory leak: CGDataProviderCreateWithCopyOfData - ios

I make use of the UIImage+Resize library to resize my images directly after taking them.
I do get a memory leak with the following code:
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, srcSize.width, srcSize.height), imgRef);
UIImage* resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGDataProviderCreateWithCopyOfData memory leak in Instruments.
I tried adding it into a #autoreleasepool as per other StackOverflow posts.

Note: You've probably have already fixed your problem, this answer for future readers having the same problem.
I've figured out what's going on my own implementation (video analysis with GPUImage).
I needed to add an autoreleasepool closure around the call to CGBitmapContextCreateImage caller (it also calls CGDataProviderCreateWithCopyOfData).
func run() {
while (parent != nil) {
if semaphore.acquireSemaphore(5000) {
autoreleasepool { // Without this I was leaking images until crash.
parent?.analyseImage(self.sampleBuffer!, lineFinder:finder) //This calls CGBitmapContextCreateImage
}
}
}
thread = nil
}

Related

Receive Memory warning in combining two UIImage

I try to combine two UIImage with the following code:
- (void)combineImage:(UIImage *)image WithFrame:(CGRect)frame Completion:(ImageProcessorCompletionBlock)block {
__weak typeof(self) wSelf = self;
dispatch_async(_queue, ^{
if (wSelf) {
typeof(wSelf) sSelf = wSelf;
UIGraphicsBeginImageContextWithOptions(sSelf.originalImage.size, NO, 0.0);
[sSelf.originalImage drawInRect:CGRectMake(0, 0, sSelf.originalImage.size.width, sSelf.originalImage.size.height)];
[image drawInRect:frame];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block(result);
}
});
}
});
}
That works but when I check out the usage of memory, it scared me. Every time I run the method the memory rise up and never release. Sometimes I receive the memory warning. Can anyone tell me why and give me a solution to solve the problem? Thanks a lot!
Finally I figure out the problem.
UIGraphicsBeginImageContextWithOptions(sSelf.originalImage.size, NO, 0.0);
The first parameter is the size of the image and the last one is the scale factor. At the beginning I have already set the image size same as the original one. But I also set the scale as 0.0, which means it is set to the scale factor of the device’s main screen. So the result image is enlarged.
If I run the code several times, the result's size gets bigger and bigger, finally it use up the memory and I receive the warning.

How to release memory quickly inside receiving method?

In my iPhone app, I have a large image that I've cached to disk and I retrieve it just before I hand the image to a class that does a lot processing on that image. The receiving class only needs the image briefly for some initialization and I want to release the memory that the image is taking up as soon as possible because the image processing code is very memory intensive, but I don't know how.
It looks something like this:
// inside viewController
- (void) pressedRender
{
UIImage *imageToProcess = [[EGOCache globalCache] imageForKey:#"reallyBigImage"];
UIImage *finalImage = [frameBuffer renderImage:imageToProcess];
// save the image
}
// inside frameBuffer class
- (UIImage *)renderImage:(UIImage *)startingImage
{
CGContextRef context = CGBitmapCreateContext(....)
CGContextDrawImage(context, rect, startingImage.CGImage);
// at this point, I no longer need the image
// and would like to release the memory it's taking up
// lots of image processing/memory usage here...
// return the processed image
CGImageRef tmpImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage *renderedImage = [UIImage imageWithCGImage:tmpImage];
CGImageRelease(tmpImage);
return renderedImage;
}
This may be obvious, but I'm missing something. Thank you.
#Jonah.at.GoDaddy is on the right track, but I would make all of this more explicit rather than relying on ARC optimizations. ARC is much less aggressive in debug mode, and so your memory usage may become too high when you're debugging unless you take steps.
UIImage *imageToProcess = [[EGOCache globalCache] imageForKey:#"reallyBigImage"];
First, I'm going to assume that imageForKey: does not cache anything itself, and does not call imageNamed: (which caches things).
The key is that you need to nil your pointer when you want the memory to go away. That's going to be very hard if you pass the image from one place to another (which Jonah's solution also fixes). Personally, I'd probably do something like this to get from image->context as fast as I can:
CGContextRef CreateContextForImage(UIImage *image) {
CGContextRef context = CGBitmapCreateContext(....)
CGContextDrawImage(context, rect, image.CGImage);
return context;
}
- (void) pressedRender {
CGContextRef context = NULL;
// I'm adding an #autoreleasepool here just in case there are some extra
// autoreleases attached by imageForKey: (which it's free to do). It also nicely
// bounds the references to imageToProcess.
#autoreleasepool {
UIImage *imageToProcess = [[EGOCache globalCache] imageForKey:#"reallyBigImage"];
context = CreateContextForImage(imageToProcess);
}
// The image should be gone now; there is no reference to it in scope.
UIImage *finalImage = [frameBuffer renderImageForContext:context];
CGContextRelease(context);
// save the image
}
// inside frameBuffer class
- (UIImage *)renderImageForContext:(CGContextRef)context
{
// lots of memory usage here...
return renderedImage;
}
For debugging, you can make sure that the UIImage is really going away by adding an associated watcher to it. See the accepted answer to How to enforce using `-retainCount` method and `-dealloc` selector under ARC? (The answer has little to do with the question; it just happens to address the same thing you might find useful).
you can autorelease objects right away in the same method. I think you need to try to handle the "big-image" process within one methods to use #autorelease:
-(void)myMethod{
//do something
#autoreleasepool{
// do your heavy image processing and free the memory right away
}
//do something
}

Received memory warning when capturing screen and save to video ios

I am now writing a program to capture screen and convert to video. I can successfully save the video if it is less than 10 seconds. But, if more than that, I received memory warning and application crash. I wrote this code as follow. Where am I missing to release data ? I would like to know how to do.
-(void)captureAndSaveImage
{
if(!stopCapturing){
if (assetWriterInput.readyForMoreMediaData)
{
keepTrackOfBackGroundMood++;
NSLog(#"keepTrackOfBackGroundMood is %d",keepTrackOfBackGroundMood);
CVReturn cvErr = kCVReturnSuccess;
CGSize imageSize = screenCaptureAndDraw.bounds.size;
CGFloat imageScale = 0; //if zero, it reduce processing time
if (NULL != UIGraphicsBeginImageContextWithOptions)
{
UIGraphicsBeginImageContextWithOptions(imageSize, NO, imageScale);
}
else
{
UIGraphicsBeginImageContext(imageSize);
}
[self.hiddenView.layer renderInContext:UIGraphicsGetCurrentContext()];
[self.screenCaptureAndDraw.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
image = (CGImageRef) [img CGImage];
CVPixelBufferRef pixelBuffer = NULL;
CFDataRef imageData= CGDataProviderCopyData(CGImageGetDataProvider(image));
cvErr = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,
FRAME_WIDTH2,
FRAME_HEIGHT2,
kCVPixelFormatType_32BGRA,
(void*)CFDataGetBytePtr(imageData),
CGImageGetBytesPerRow(image),
NULL,
NULL,
NULL,
&pixelBuffer);
//CFRelease(imageData);
//CGImageRelease(image); //I can't write this code because I am not creating it and when I check online, it say it is not my responsibility to release. If I write, the application crash immediately
// calculate the time
CFAbsoluteTime thisFrameWallClockTime = CFAbsoluteTimeGetCurrent();
CFTimeInterval elapsedTime = thisFrameWallClockTime - firstFrameWallClockTime;
// write the sample
BOOL appended = [assetWriterPixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:presentationTime];
if (appended) {
NSLog (#"appended sample at time %lf and keepTrackofappended is %d", CMTimeGetSeconds(presentationTime),keepTrackofappended);
keepTrackofappended++;
} else {
NSLog (#"failed to append");
[self stopRecording];
//self.startStopButton.selected = NO;
screenRecord=false;
}
}
}//stop capturing
// });
}
I agree that you don't want to do the CGImageRelease(image). This object was created by calling CGImage method of a UIImage object. Thus ownership was not transferred and ARC still does the memory management for your img object and no releasing of the image object is needed.
But I think you do want to restore your CFRelease(imageData). This is an object created by CGDataProviderCopyData, so you own it and must clean up.
I also think you have to release the pixelBuffer that you created with CVPixelBufferCreateWithBytes after you appendPixelBuffer. You can use the CVPixelBufferRelease function for that.
The Core Foundation memory rule is that if the function has Copy or Create in the name, you own that object and are responsible for releasing it. See the Create Rule in the Memory Management Programming Guide for Core Foundation.
I would have thought that the static analyzer (shift+command+B or "Analyze" from the Xcode "Product" menu) would have identified this issue, as it has gotten much better at finding Core Foundation memory issues (albeit, not perfect).
Alternatively, if you run your app through the Leaks tool in Instruments (which will also show you the Allocations tool at the same time), you can take a look at your memory usage. While the video capture requires a lot of Live Bytes, in my experience it stays pretty darn flat. If it's growing, you have a leak somewhere.

UIGraphicsGetImageFromCurrentImageContext leak

I have written the following code snippet to take screen snapshot:
UIGraphicsBeginImageContext(animationView.frame.size);
[[window layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage* screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
But, UIGraphicsGetImageFromCurrentImageContext seems to be leaking. Is this correct?
In Instruments I could not get the exact leak point. In activity monitor I observed that when I switch to the UI that executes the above code snippet memory increments by some MB. After this point it never decreases.
Does UIGraphicsGetImageFromCurrentImageContext has memory leak? How do I solve this?
Edit
Instruments analysis
Activity Monitor: shows the memory hike when this line of code is executed; never decreases even after releasing screenshot (UIImage)
Leaks and allocation, Heap Snapshot: Does not show any leak OR this allocation.
You have just created a UIImage with data for your animationView (which could be some MB). Perhaps you should wrap this functionality in an autorelease pool.
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
UIImage* screenshot = UIGraphicsGetImageFromCurrentImageContext();
//[screenshot retain]; //If you want precise control over when it is released and you will use it later.
[pool release];
CGContextRef context = UIGraphicsGetCurrentContext();
/* you code */
CGContextRelease(context);
clear the context when done

UIGraphicsGetImageFromCurrentImageContext memory leak with previews

I'm trying to create previews images of pages in a PDF
but I have some problems with the release of memory.
I wrote a simple test algorithm that cycles on the problem,
the app crashes near the 40th iteration:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:#"myPdf.pdf"];
CFURLRef url = CFURLCreateWithFileSystemPath( NULL, (CFStringRef)pdfPath, kCFURLPOSIXPathStyle, NO );
CGPDFDocumentRef myPdf = CGPDFDocumentCreateWithURL( url );
CFRelease (url);
CGPDFPageRef page = CGPDFDocumentGetPage( myPdf, 1 );
int i=0;
while(i < 1000){
UIGraphicsBeginImageContext(CGSizeMake(768,1024));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,CGRectMake(0, 0, 768, 1024));
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0, 1024);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawPDFPage(context, page);
CGContextRestoreGState(context);
// --------------------------
// The problem is here (without this line the application doesn't crash)
UIImageView *backgroundImageView1 = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
// --------------------------
UIGraphicsEndImageContext();
[backgroundImageView1 release];
NSLog(#"Loop: %d", i++);
}
CGPDFDocumentRelease(myPdf);
The above-mentioned line seems to generate a memory leak,
however, instruments doesn't show memory problems;
Can I escape from this kind of mistake?someone can explain me in which way?
Are there other ways to show previews of a pdf?
UPDATE
I think the problem isn't the release of UIImage created by the method UIGraphicsGetImageFromCurrentImageContext() but the release of UIImageView created with this autorelease image.
I have divided the line of code in three steps:
UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIImageView *myImageView = [[UIImageView alloc] init];
[myImageView setImage: myImage]; // Memory Leak
The first and second lines doesn't create memory leaks so I think that the method UIGraphicsGetImageFromCurrentImageContext is not the problem.
I also tried as follows but the problem persists:
UIImageView *myImageView = [[UIImageView alloc] initWithImage:myImage];
I think there is a memory leak in the release of a UIImageView that contains a UIImage with the autorelease property.
I tried to write my object UIImageView inheriting a UIView as explained in this thread.
This solution works but isn't very elegant, it's a workaround, I would prefer to use the object UIImageView solving the memory problem.
The problem is this:
UIGraphicsGetImageFromCurrentImageContext()
returns an autoreleased UIImage. The autorelease pool holds on to this image until your code returns control to the runloop, which you do not do for a long time. To solve this problem, you would have to create and drain a fresh autorelease pool on every iteration (or every few iterations) of your while loop.
I know it's an old question, but I've just been banging my head against the wall on this for a few hours. In my app repeatedly calling
UIImage *image = UIGraphicsGetImageFromCurrentImageContext()
in a loop does hold on to the memory despite me calling image = nil; Not sure how long the app would keep hold of the memory before freeing, but it's certainly long enough for my app to get a memory warning then crash.
I managed to solve it finally by wrapping the code that calls / uses the image from UIGraphicsGetImageFromCurrentImageContext() in #autoreleasepool. So I have:
#autoreleasepool {
UIImage *image = [self imageWithView:_outputImageView]; //create the image
[movie addImage:image frameNum:i fps:kFramesPerSec]; //use the image as a frame in movie
image = nil;
}
Hope that might help someone.
For future reference here's what I did to solve this (tested in Swift 4).
I was calling the function below for every new image downloaded from the internet (on a utility queue). Before implementing the autorelease pool it would crash after processing about 100.
For simplicity, in the resizeImage function I've removed needed code except for the autoreleasepool and the part that was leaking.
private func resizeImage(image: UIImage, toHeight: CGFloat) -> UIImage {
return autoreleasepool { () -> UIImage in
[...]
let newImage = UIGraphicsGetImageFromCurrentImageContext() //Leaked
UIGraphicsEndImageContext()
return newImage!
}
}
I hope this helps!
For those who tried all solution above and still has a memory leak, check if you are using a dispatch queue. If so, be sure to set its autoreleaseFrequency to .workItem. Or the autorelease pool you set up inside the will not execute.
DispatchQueue(label: "imageQueue", qos: .userInitiated, autoreleaseFrequency: .workItem)
Hope it helps, it has bugged me for hours until I finally realize that's DispatchQueue that is holding the block.
Is this code running on the main thread? The documentation of the UIGraphicsGetImageFromCurrentImageContext (link) says it must run that way.
your line of crash you can update it like following
get one UIimage out of loop
rendered_image = UIGraphicsGetImageFromCurrentImageContext();

Resources