Correct way to handle CGImageRef in containers under ARC - ios

I am new to incorporating arc in my projects. I am trying to understand __bridge and its little friends so that I can properly cast my CGImageRef's when adding and removing them from containers.
I am getting a "Potential leak of an object stored…" on one of my lines. Here is the basic cycle of my code:
CGImageRef renderedRef = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());
[_array addObject: (__bridge_transfer id)renderedRef];//_array is an iVar
then somewhere down the road I do this:
CGImageRef iRef = (__bridge_retained CGImageRef)array[0];
//then I do something fancy with iRef
//at the end of the method, I get "Potential leak of an object stored…"
//I have no idea what to do
//I've tried CGImageRelease(iRef); but it still doesn't change the warning.
Can someone shed some light on this? Also, I have tried just using __bridge but that doesn't make a difference.
Edit 1:
I expanded the analyzer results and followed what was happening. It was because I was using iRef in a method like so: [self doSomethingFancy:iRef]; and in that method, iRef was being retained but not released. So that fixes the warning, but I'm still a bit puzzled.
I am not quite clear on when to use the various __bridge casts. Under ARC, does the following increase the reference count?
CGImageRef iRef = (__bridge CGImageRef)array[0];
Also, at some point if I tell my _array iVar to removeAllObjects, will that decrement their reference counts properly?

// This WILL NOT increment the image's retain count.
CGImageRef iRef = (__bridge CGImageRef)array[0];
// This WILL increment the image's retain count.
CGImageRef iRef = (__bridge_retained CGImageRef)array[0];
Since __bridge_retained increments the retain count, you need to decrement the retain count at some point later. Because __bridge_retained acts like CFRetain, Apple created a __bridge_retained wrapper called CFBridgingRetain that reminds you of this:
// This WILL increment the image's retain count.
CGImageRef iRef = CFBridgingRetain(array[0]);
Whenever you see CFRetain or CFBridgingRetain, you know you need to release the object at some point later.
Similarly, you can use CFBridgingRelease instead of __bridge_transfer to decrement the retain count of a CF object. Example:
[_array addObject:CFBridgingRelease(renderedRef)];
You can use CFBridgingRelease to balance a CFRetain or a CFBridgingRetain. It returns an id that ARC manages.
A normal NSMutableArray retains each of its elements. You can tell it to become empty:
[_array removeAllObjects];
When you do this, it will release each of its elements, balancing the retain it performed on each element.
Thus there is no memory leak in this code:
CGImageRef image = CGImageCreate(...);
[_array addObject:CFBridgingRelease(image)];
[_array removeAllObjects];
or in this code:
CGImageRef image = CGImageCreate(...);
[_array addObject:CFBridgingRelease(image)];
CGImageRef image2 = CFBridgingRetain([_array lastObject]);
[_array removeLastObject];
CGContextDrawImage(gc, rect, image2);
CGImageRelease(image2);

Related

Need to release a CGImage contained in a CALayer's contents property

I have a CALayer object called sublayer. I use it as self.sublayer throughout my view controller because I have made it a property in my view controller's header file.
I set the sublayer's contents property equal to a UIImage object that is created using a CGImageRef object called imageRef:
self.subLayer.contents = (id)[UIImage imageWithCGImage:imageRef].CGImage;
I then release the imageRef object right away now that it has been used to create the sublayer contents and it is no longer needed:
CGImageRelease(imageRef);
However, here is what is bothering me. Later on in the code I will no longer need self.sublayer.contents and I want to make sure I release the CGImage it contains properly.
If I NSLog self.sublayer.contents it will print this to the console: <CGImage 0x146537c0>
So I need to be able to release this CGImage as well.
I tried using this to release the CGImage, but the NSLog still prints the same to the console:
CGImageRelease((__bridge CGImageRef)(self.subLayer.contents));
If I use this, the NSLog will print to the console as (null), but I am worried that this is technically not releasing the CGImage:
self.subLayer.contents = nil;
Does setting the sublayer's contents property to nil properly release the CGImage, or am I correct in thinking that it is not technically releasing the CGImage?
I am experiencing memory problems right now in my app so I need to make sure that I am releasing this CGImage properly.
The contents property on CALayer is a retaining property, meaning that it's setter implementation more or less does this:
- (void)setContents:(id)contents
{
if (contents == _contents) return; // Same as existing value
[_contents release];
_contents = [contents retain];
}
So, when you set nil as the new contents, the old contents is released.

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
}

imageWithCGImage: GCD memory issue

When I perform the following only on the main thread iref gets autoreleased immediately:
-(void)loadImage:(ALAsset*)asset{
#autoreleasepool {
ALAssetRepresentation* rep = [asset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
UIImage* image = [UIImage imageWithCGImage:iref
scale:[rep scale]
orientation:UIImageOrientationUp];
[self.imageView setImage:image];
}
}
But when I perform imageWithCGImage: with GCD on a background thread iref does not get released instantly like in the first example. only after about a minute:
-(void)loadImage:(ALAsset*)asset{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
#autoreleasepool {
ALAssetRepresentation* rep = [asset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
UIImage* image = [UIImage imageWithCGImage:iref
scale:[rep scale]
orientation:UIImageOrientationUp];
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.imageView setImage:image];
});
}
});
}
How can I make the CGImageRef object to be released immediately?
Prior Research:
The Leak instrument doesnt show any leaks when I record with it.
The Allocations instruments shows that a CGImageRef object was allocated and is still living for about a minute after it should have been released.
If I try to manually release the CGImageRef object using CGImageRelease I get a BAD_EXEC exception after a minute when the image tries to get autoreleased.
retaining the iref using CGImageRetain and then using CGImageRelease to release it doesnt work.
similar questions on stackoverflow that didn't help: image loading with gcd, received memory warning in create image, memory leak when get fullscreenimage from alaseet result,
First off, there's not a notion of an "autoreleased" CF object. You can get into situations where such a thing exists when dealing with toll-free bridged classes, but as you can see, there's a CFRetain and a CFRelease but no CFAutorelease. So I think you're misconstruing the ownership of iref. Let's trace ownership throughout this code:
-(void)loadImage:(ALAsset*)asset{
asset is passed into this method. Its retain count is presumed to be at least 1.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
The block closure takes a retain on asset.
#autoreleasepool {
ALAssetRepresentation* rep = [asset defaultRepresentation];
This returns you, by naming convention, an object that you don't own. It might be autoreleased, it might be a singleton/global, etc. But you don't own it and shouldn't take ownership of it by retaining it.
CGImageRef iref = [rep fullScreenImage];
Since there's not a notion of an "autoreleased" CF object, we'll presume that rep is returning you an inner pointer to a CGImageRef owned by rep. You also don't own this, and shouldn't retain it. Relatedly you don't control when it will go away. A reasonable guess would be that it will live as long as rep and a reasonable guess is that rep will live as long as asset so you should probably assume that iref will live at least as long as asset.
UIImage* image = [UIImage imageWithCGImage:iref
scale:[rep scale]
orientation:UIImageOrientationUp];
If the UIImage needs the CGImageRef to stick around, it will take a retain or make a copy to ensure that it stays alive. (Probably the latter.) The UIImage itself is autoreleased, by naming convention.
dispatch_async(dispatch_get_main_queue(), ^(void) {
This inner block closure is going to take a retain on image (and self). The block will be copied by libdispatch extending the life of those retains until the block is executed and itself released.
[self.imageView setImage:image];
The image view is going to take a retain (or copy) on image if it needs to, in order to do its job.
});
The inner block is done executing. At some point in the future, libdispatch will release it, which will transitively release the retains taken by the block closure on self and image.
}
Your autorelease pool pops here. Anything that was implicitly retain/autoreleased should be released now.
});
The outer block is done executing. At some point in the future, libdispatch will release it, which will transitively release the retain taken by the block closure on asset.
}
Ultimately, this method cannot control the lifetime of the CGImageRef in iref because it never has ownership of it. The implication here is that the CGImageRef is transitively owned by asset, so it will live at least as long as asset. Since asset is retained by virtue of being used in the outer block (i.e retained by the outer block's closure) and since libdispatch makes no promises about when finished blocks will be released, you effectively can't guarantee that iref will go away any sooner than libdispatch gets around to it.
If you wanted to go to manual retain/release, and be as explicit about it as possible, you could do this:
-(void)loadImage:(ALAsset*)asset{
__block ALAsset* weakAsset = [asset retain]; // asset +1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
#autoreleasepool {
ALAssetRepresentation* rep = [weakAsset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
UIImage* image = [[UIImage alloc] imageWithCGImage:iref
scale:[rep scale]
orientation:UIImageOrientationUp];
__block UIImage* weakImage = [image retain]; // image +1
[weakAsset release]; // asset -1
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self.imageView setImage: weakImage];
[weakImage release]; // image -1
});
}
});
}
__block prevents the block closures from retaining asset and image allowing you to explicitly retain/release them yourself. This will mean that of all the things you create, all will be explicitly disposed. (rep and image are presumably retain/autoreleased, but your pool should take care of those.) I believe this is the most explicit you can be about this, given that asset is passed in to you, and therefore you don't control how long it lives, and it is ultimately the "owner" (as far as this scope is concerned) of the CGImageRef stored in iref.
Hope that clarifies things a bit.
You should retain the CGImageRef
..
CGImageRef iref = CGImageRetain([rep fullScreenImage]);
//..before [self.imageView..
CGImageRelease(iref)
The rest is just a matter of run loop, in one without GCD the image is released istanneously, in the other is managed by GCD, but is wrong anyway, someone has to take the ownership of iref.

Rules for managing CGImageRef memory?

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.

Should retain count increase after an image rotation?

I'm using the following code to rotate an image
http://www.platinumball.net/blog/2010/01/31/iphone-uiimage-rotation-and-scaling/
that's one of the few image transformations that I do before uploading an image to the server, I also have some other transformations: normalize, crop, resize.
Each one of the transformations returns an (UIImage*) and I add those functions using a category. I use it like this:
UIImage *img = //image from camera;
img = [[[img normalize] rotate] scale] resize];
[upload img];
After selecting 3~4 photos from the camera and executing the same code each time I get a Memory Warning message in XCode.
I'm guessing I have a memory leak somewhere (even though im using ARC). I'm not very experienced using the xCode debugging tools, so I started printing the retain count after each method.
UIImage *img = //image from camera;
img = [img normalize];
img = [img rotate]; // retain count increases :(
img = [img scale];
img = [img resize];
The only operation that increases the retain count is the rotation. Is this normal?
The only operation that increases the retain count is the rotation. Is this normal?
It's quite possible that the UIGraphicsGetImageFromCurrentImageContext() call in your rotate function ends up retaining the image. If so, it almost certainly also autoreleases the image in keeping with the normal Cocoa memory management rules. Either way, you shouldn't worry about it. As long as your rotate function doesn't itself contain any unbalanced retain (or alloc, new, or copy) calls, you should expect to be free of leaks. If you do suspect a leak, it's better to track it down with Instruments than by watching retainCount yourself.

Resources