Rules for managing CGImageRef memory? - ios

What are the rules for managing memory for CGImageRefs with ARC? That is, can someone help me to the right documentation?
I am getting images from the photo library and creating a UIImage to display:
CGImageRef newImage = [assetRep fullResolutionImage];
...
UIImage *cloudImage = [UIImage imageWithCGImage:newImage scale:scale orientation:orientation];
Do I need to do CGImageRelease(newImage)?
I'm getting memory warnings but it doesn't seem to be a gradual buildup of objects I haven't released and I'm not seeing any leaks with Instruments. Puzzled I am.

No, you do not need to call CGImageRelease() on the CGImageRef returned by ALAssetRepresentation's convenience methods like fullResolutionImage or fullScreenImage. Unfortunately, at the current time, the documentation and header files for these methods does not make that clear.
If you create a CGImageRef yourself by using one of the CGImageCreate*() functions, then you own it and are responsible for releasing that image ref using CGImageRelease(). In contrast, the CGImageRefs returned by fullResolutionImage and fullScreenImage appear to be "autoreleased" in the sense that you do not own the image ref returned by those methods. For example, say you try something like this in your code:
CGImageRef newImage = [assetRep fullResolutionImage];
...
UIImage *cloudImage = [UIImage imageWithCGImage:newImage
scale:scale orientation:orientation];
CGImageRelease(newImage);
If you run the static analyzer, it will issue the following warning for the CGImageRelease(newImage); line:
Incorrect decrement of the reference count of an object that is not
owned at this point by the caller
Note that you will get this warning regardless of whether your project is set to use Manual Reference Counting or ARC.
In contrast, the documentation for the CGImage method of NSBitmapImageRep, for example, makes the fact that the CGImageRef returned is autoreleased more clear:
CGImage
Returns a Core Graphics image object from the receiver’s
current bitmap data.
- (CGImageRef)CGImage
Return Value
Returns an autoreleased CGImageRef opaque type based on the receiver’s
current bitmap data.

Related

iOS - CGImageRef Potential Leak

I have this code:
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], rect);
UIImage *outputimage = [UIImage imageWithCGImage:imageRef
scale:image.scale
orientation:UIImageOrientationUp];
and I get the warning:
Object leaked: object allocated and stored into 'imageRef' is not
referenced later in this execution path and has a retain count of +1
But I am using ARC and cannot use release or autoRelease. How to resolve this?
Just add this code
CGImageRelease(imageRef);
From CGImageCreateWithImageInRect document,
The resulting image retains a reference to the original image, which means you may release the original image after calling this function.
So,what you need to do is just call CGImageRelease to make it retain count -1

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
}

returning a UIImage where data behind it is released causing bad_access

I'm using ARC. I have something like this:
-(UIImage *)buildFullResImage
{
// blah blah does stuff to make a CGImage ref from some data...
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
// then make the uiimage from that
UIImage *myImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpaceRef);
free(buffer);
return myImage;
}
Then somewhere else I do this...
UIImage *thisImage = [self buildFullResImage];
UIImage *resizedImage = [self imageWithImage:thisImage scaledToSize:CGSizeMake(newWidth, newHeight)];
And in the imageWithImage method I'm getting a bad_access crash because the actual image data (in buffer) was released back in buildFullResImage. I've tried a couple of different imageWithImage methods that can easily be found here on SO. It always crashes on whatever is accessing the passed uiimage's data. Usually it's crashing on like a:
CGContextDrawImage(bitmap, newRect, imageRef);
or
CGContextDrawImage(thisContext, CGRectMake(0.0, 0.0, imageSize.width, imageSize.height), image);
The UIImage when it comes across has an width and height and an address (is not null) but apparently no data behind it... because I released the data before returning the image. You can't release it after returning it.
I don't want to NOT release the buffer and the imageRef and have a leak. I don't want to duplicate code or merge the resizing with the building as I won't always need to use the resized version. I wanted modular code.
I know this seems like objective-c 101 but I've been fighting with it for hours. I have a dumb quick work around but I'd like to know the right way to do this.
UPDATE..
My dumb work around which is working is that I made buffer, the myImage, and the imageRef all global properties so they stay retained until I'm ready to release them. I can manage to release them after I'm done using them to save off the image or whatever I'm doing with it. But it seems like there would be a way to make the UIImage that I'm returning be it's own object with it's own control of it's underlying data.

UIImage from CGImageRef

I am trying a simple test for a much more complex project but I am baffled as to why the code below is crashing and giving an EXC_BAD_ACCESS error?
This is called from a UIView.
- (void)testing {
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"ball.png" ofType:nil];
CGImageRef imageRef = [[[UIImage alloc]initWithContentsOfFile:imagePath]CGImage];
// CGImageRetain(imageRef);
UIImage *newImage = [[UIImage alloc]initWithCGImage:imageRef];
UIImageView *iv = [[UIImageView alloc]initWithImage:newImage];
[self addSubview:iv];
}
My guess is that the CGImageRef is not being retained but adding CGImageRetain(imageRef); makes no difference.
I should also note that this project has ARC turned on.
EDIT
I did a little bit more testing and have discovered that this is directly related to ARC as I created 2 basic projects including only the code above. The first with ARC turned off and it worked perfectly. The next with ARC turned on and BAM crash with the same error. The interesting thing is that I got an actual log error ONLY the first time I ran the project before the crash.
Error: ImageIO: ImageProviderCopyImageBlockSetCallback 'ImageProviderCopyImageBlockSetCallback' header is not a CFDictionary...
This line is the problem:
CGImageRef imageRef = [[[UIImage alloc]initWithContentsOfFile:imagePath]CGImage];
The created UIImage will be released immediately following this full-expression (e.g. after this line). So even trying to add a CGImageRetain() afterwards won't work.
The fundamental problem is the CGImageRef returned from CGImage is almost certainly an ivar of the UIImage and will be released when the UIImage is deallocted.
The generic way to fix this is to extend the lifetime of the UIImage. You can do this by placing the UIImage into a local variable and referencing it after your last reference to the CGImage (e.g. with (void)uiimageVar). Alternatively, you can retain the CGImageRef on that same line, as in
CGImageRef imageRef = CGImageRetain([[[UIImage alloc] initWithContentsOfFile:imagePath] CGImage]);
But if you do this, don't forget to release the imageRef when you're done.
You wrote this:
CGImageRef imageRef = [[[UIImage alloc]initWithContentsOfFile:imagePath]CGImage];
That line creates a UIImage, gets its CGImage property, and then releases the UIImage object. Since you're not retaining the CGImage on that line, the only owner of the CGImage is the UIImage. So when the UIImage is released and immediately deallocated, it deallocates the CGImage too.
You need to retain the CGImage before the UIImage is released. Try this:
CGImageRef imageRef = CGImageRetain([[UIImage alloc]initWithContentsOfFile:imagePath].CGImage);
and then at the end of the function:
CGImageRelease(imageRef);

handle memory warning while applying core image filter

I am using the followinf code for applying image filters. This is working fine on scaled down images.
But when I apply more than 2 filters on full resolution images, the app crashes. A memory warning is received.
When I open the 'allocations' instrument, I see that CFData(store) takes up most of the memory used by the program.
When I apply more than 2 filters on a full resolution image, the 'overall bytes' go upto 54MB. While the 'live bytes' don't seem to reach more than 12MB when I use my eyes on the numbers as such, but the spikes show that live bytes also reach upto this number and come back.
Where am i going wrong?
- (UIImage *)editImage:(UIImage *)imageToBeEdited tintValue:(float)tint
{
CIImage *image = [[CIImage alloc] initWithImage:imageToBeEdited];
NSLog(#"in edit Image:\ncheck image: %#\ncheck value:%f", image, tint);
[tintFilter setValue:image forKey:kCIInputImageKey];
[tintFilter setValue:[NSNumber numberWithFloat:tint] forKey:#"inputAngle"];
CIImage *outputImage = [tintFilter outputImage];
NSLog(#"check output image: %#", outputImage);
return [self completeEditingUsingOutputImage:outputImage];
}
- (UIImage *)completeEditingUsingOutputImage:(CIImage *)outputImage
{
CGImageRef cgimg = [context createCGImage:outputImage fromRect:outputImage.extent];
NSLog(#"check cgimg: %#", cgimg);
UIImage *newImage = [UIImage imageWithCGImage:cgimg];
NSLog(#"check newImge: %#", newImage);
CGImageRelease(cgimg);
return newImage;
}
Edit:
I also tried making cgimg as nil. Didn't help.
I tried putting context declaration and definition inside the 2nd function. Didn't help.
I tried to move declarations and definitions of filters inside the functions, didn't help.
AlsoCrash happens at
CGImageRef cgimg = [context createCGImage:outputImage fromRect:outputImage.extent];
the cgimg i was making took most of the space in memory and was not getting released.
I observed that calling the filer with smaller values takes the CFData (store) memory value back to a samller value, thus avoiding the crash.
So I apply filter and after that call the same filter with image as 'nil'. This takes memory back to 484kb or something from 48 MB after applying all 4 filters.
Also, I am applying this filters on a background thread instead of the main thread. Applying on main thread again causes crash. Probably it doesn't get enough time to release the memory. I don't know.
But these things are working smoothly now.
// where is your input filter name like this:
[tintFilter setValue:image forKey:#"CIHueAdjust"];
// I think you have a mistake in outputImage.extent. You just write this
CGImageRef cgimg = [context createCGImage:outputImage fromRect:outputImage extent];

Resources