UIImage from CGImageRef - ios

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

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

EXC_BAD_ACCESS on accessing UIImage's size property

The image is an instance of UIImage. The first line executes with no problems, but the second one gives an EXC_BAD_ACCESS error at runtime.
NSLog(#"SCALE: %f", image.scale);
NSLog(#"TEST: %#", NSStringFromCGSize(image.size));
I can view the values of size property in Xcode by mouse-hovering it though.
Can you please help me in understanding what's wrong with it and/or what I might be missing?
Tested on a device and in simulator running iOS 8.
UPD: This is how I'm creating the image:
ALAssetRepresentation *rep = [asset defaultRepresentation];
CGFloat scale = 1.0f;
CGImageRef imageRef = [rep fullResolutionImage];
UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale orientation:(UIImageOrientation)rep.orientation];
CGImageRelease(imageRef);
I just tried to delete the last line CGImageRelease(imageRef); and it seem to be fixed the problem. But I do need to release the CGImageRef, since I'm loading very large photos inside a loop and that takes a lot of memory.
So, I figured it out. The problem was that I released a CGImageRef that I didn't own.
CGImageRef imageRef = [rep fullResolutionImage];
If you get a CGImageRef by calling fullResolutionImage, you don't own it. Therefore, you don't need to release it yourself neither.
CGImageRelease(imageRef);
Removing the last line fixed the problem for me.

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.

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.

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