imageWithCGImage: GCD memory issue - ios

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.

Related

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
}

Correct way to handle CGImageRef in containers under ARC

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

How to avoid "Received Memory warning" when changing the slider value in order to change the intensity of the image(coreImage)?

I am trying to implement a method that changes the tone of an image in accordance with the slider value, but when i change the slider value continuously it is showing memory warning and the app crashes.
This is my sample code, I tried using dispatch_async
-(void)valueChanged
{
float slideValue = slider.value;
NSLog(#"%0.f",slideValue);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0.5), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[filter setValue:#(slideValue)
forKey:#"inputIntensity"];
CIImage *outputImage = [filter outputImage];
CGImageRef cgimg = [context createCGImage:outputImage
fromRect:[outputImage extent]];
UIImage *newImage = [UIImage imageWithCGImage:cgimg];
imageView.image = newImage;
});
});
}
You are leaking memory, what means that you are allocating objects that you are not releasing, hence the memory warnings.
Have you tried releasing the images after you have used them?
CGImageRelease(cgimg)
At the end of the method
Check the documentation for createCGImage:fromRect: API, for return value it says,
You are responsible for releasing the returned image when you no longer need it.
Which means you are leaking memory for cgimg instance. As suggested by Antonio MG you should release the reference at the end. The imageWithCGImage: will retain it, so no need to worry.
Hope that helps!

UIImage setImage is very, very slow

I'm trying to load an image from a local URL using the following:
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:fileURL]];
[self.imageView setImage:image];
NSLog(#"imageView set");
So I see in the console "imageView set" almost immediately, but it takes a very long time for it to be reflected in the UI (sometimes a few minutes!).
Any idea why this is happening?
This happened to me when I was setting the image in a background thread (in which I was downloading the image file). Just make sure your code is run in the main thread. The image will change immediately.
// In a background thread...
UIImage *image = ... // Build or download the image
dispatch_async(dispatch_get_main_queue(), ^{
[self.imageView setImage:image]; // Run in main thread (UI thread)
});
You should load up instruments and see what exactly it is doing.
For starters, you should do what you can to avoid I/O on the drawing thread. Also, are there other I/O requests at the same time?
A UIImage does not necessarily need to be a singular bitmap representation -- it may be backed by a cache and/or loaded lazily. So just because an 'image' is 'set', does not mean that the optimal bitmap has been loaded into memory and prepared for rendering -- it may be deferred until render (draw) is requested.
Profiling OTOH will tell you (generally) why it is taking longer than expected.
Image loading from NSData takes more time.
Instead you can simply use the following code:
UIImage *image = [UIImage imageWithContentsOfFile:fileURL];
Try it.
So I tried the following:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:fileURL]];
[self.imageView setImage:image];
NSLog(#"imageView set");
});
And it is quite a bit faster. Not sure why though.

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.

Resources