Unable to release a CGContextRef (context is from a CGLayer) - iphone

When you create a CGLayer like so, and then get the context...it appears to be impossible to release the CGContextRef?
Releasing the CGLayerRef itself (apparently) works fine.
You'd think you could release the CGContextRef just before releasing the CGLayer - but no? Nor can you release the CGContextRef just after releasing the CGLayer.
If you release the CGContextRef, the app crashes.
CGLayerRef aether = CGLayerCreateWithContext(
UIGraphicsGetCurrentContext(), CGSizeMake(1024,768), NULL);
CGContextRef weird = CGLayerGetContext(aether);
// paths, strokes, filling etc
// paths, strokes, filling etc
// try releasing weird here
CGLayerRelease(aether);
// or, try releasing weird here
Does anyone know what is going on here? (Note further that CGContextRelease is indeed just the same as CFRelease, with some nil checking.)
In fact should you never manually release CGContextRef? Does anyone know? Cheers.
CGContextRelease(weird); // impossible, not necessary, doesn't work???
Regarding Joel's spectacular answer below:
Is releasing the CGLayerRef correct and proper? Joel has pointed out:
"Yes, since the function you're obtaining it from has 'Create' in its signature. See: documentation/CoreFoundation/"

You do not own the context returned from CGLayerGetContext, and so should not release it*. In particular, see http://developer.apple.com/library/mac/#documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html#//apple_ref/doc/writerid/cfGetRule for information regarding 'Get' functions in Core Foundation.
*: at least, you shouldn't release it given your example code. If you retained it first (CGContextRetain(weird)), then you should have a CGContextRelease to balance it.

Related

Potential leak of object when calling subtreebordercolor

I am getting memory leak warnings for these two methods. The second one calls the first one and apparently its leaking memory. Any ideas?
static UIColor *subtreeBorderColor(void)
{
return [UIColor colorWithRed:0.0f green:0.5f blue:0.0f alpha:1.0f];
}
- (void) updateSubtreeBorder
{
CALayer *layer = [self layer];
if (layer) {
// If the enclosing TreeGraph has its "showsSubtreeFrames" debug feature enabled,
// configure the backing layer to draw its border programmatically. This is much more efficient
// than allocating a backing store for each SubtreeView's backing layer, only to stroke a simple
// rectangle into that backing store.
PSBaseTreeGraphView *treeGraph = [self enclosingTreeGraph];
if ([treeGraph showsSubtreeFrames]) {
[layer setBorderWidth:subtreeBorderWidth()];
[layer setBorderColor:[subtreeBorderColor() CGColor]];
} else {
[layer setBorderWidth:0.0];
}
}
}
//3: Potential leak of an object
//6: Calling 'subtreeBorderColor'
//1: Entered call from 'updateSubtreeBorder'
//13: Method returns an Objective-C object with a +0 retain count
//12: Reference count incremented. The object now has a +1 retain count
//6: Returning from 'subtreeBorderColor'
//13: Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1
UPDATE 2: I just completely changed the code and deleted temporary files and cleaned the solution and this is what I see - it is finding errors where there isn't even code
Simple. You don't need the call to - retain in the function. That's exactly what the autorelease pattern is invented for. Since you don't create the UIColor object using alloc-init, you don't take ownership of it. No need to superfluously complicate memory management further. :)
Edit: (to prevent future downvotes) now that you edited your question and code so that it no longer erroneously returns a retained object, the previous statement is no longer valid. Yes, Xcode shows this notice about the memory leak where "there isn't even code", and that's strange. Yes, perhaps a compiler bug. Still, a temporary (and in my opinion, perfectly valid) workaround is to simply use a define instead of a function. Let's see what Xcode says if you write this instead:
#define SUBTREE_BORDER_COLOR [UIColor colorWithRed:0.0f green:0.5f blue:0.0f alpha:1.0f]
If you want to be using "retain" the way you are, you need to also "release" the "UIColor" object you returned from your "subtreeBorderColor" method.
Were this my code, I wouldn't do "retain" on that autoreleased object to begin with. Or better than that, simply do my code with ARC enabled.
both pervious answers are right
but, if for any reason you need to retain an object being returned by your method, please follow the rules and rename your method starting with "new" (or alloc or retain or copy), so everybody (and you too) will know that the returned object is retained, so the calling code knows it's responsable to release it later, when needed...

Managing Memory in CoreGraphics / CoreText with Return Values

I have a simple method which takes in a CTFramesetterRef object and builds a CTTextFrameRef from that as follows:
- (CTFrameRef) createFrameRefWithFramesetterRef:(CTFramesetterRef)framesetterRef
{
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetterRef, CFRangeMake(0, 0), self.mutablePathRef, NULL);
return frameRef;
}
Then in another method I make a call to the above method:
- (void) someMethod
{
CTFramesetterRef framesetterRef = CTFramesetterCreateWithAttributedString((CFMutableAttributedStringRef)self.text);
CTFrameRef newFrameRef = [self createFrameRefWithFramesetterRef:framesetterRef];
//do some stuff
CTRelease(newFrameRef);
CTRelease(framesetterRef);
}
I just wanted to verify that I managed memory properly here as I am new to working with CoreGraphics. When CTFramesetterCreateFrame is called, retain is automatically called on frameRef. I don't have to worry about releasing frameRef when it's returned since it's stored in newFrameRef and the retain count stays the same. All I have to worry about is releasing newFrameRef (CTRelease(newFrameRef)). Is this correct?
UPDATE: I'm actually still getting a memory leak from this, one at the line with the return and one where I'm releasing "newFrameRef"
C-interface API's take their cues from the old Foundarion Kit naming guide, which has two distinct (and helpful) naming conventions called the Get Rule and the Create Rule. For brevity's sake: any C-function that has the word create in it returns a reference that you must manage manually. So to answer your question, yes, you do need to release that CTFramesetterRef, because you've got a leak on your hands now.

How to access CGContext for creating CGLayer offscreen

Intention: creating CGLayer offscreen, draw some complicated stuff on it from second thread and use it later for quick drawing on main thread
Problem: CGLayerCreateWithContext(context, size, info) expects an already existing CGContext so that it knows for what kind of context it needs to be optimized.
Solution I found so far: CGContextRef ctx = UIGraphicSetCurrentContext()
but this function doesn't seem to exist anymore.
Question: Isn't there another way to access something like a default context? Or do i really need to wait for the first drawRect: call just for accessing UIGraphicsGetCurrentContext() and creating all CGLayers from main thread with one wasted drawing run?
you can create an image context by doing something like:
UIGraphicsBeginImageContext(rect);
// your drawing code
UIGraphicsEndImageContext();
with that said, i'm not sure you can do this from a thread other than main. worth a try, though.

Quartz PDF-context based function fails (occasionally) with BAD_ACCESS

Im Getting a Frequent enough BAD_ACCESS when i call this Quartz function:
CGContextDrawPDFPage ((CGContextRef)context, (CGPDFPageRef)pageRef);
Here is how i call it:
CGContextRef context = UIGraphicsGetCurrentContext();
//translate, scale
CGPDFPageRef myPageRef = CGPDFDocumentGetPage ([PDFDocument sharedPDFDocument].documentData, pageNumber);
CGContextDrawPDFPage (context, myPageRef);//BAD_ACCESS HERE
return UIGraphicsGetImageFromCurrentImageContext();//autoreleased- saved on return
//close context
Neither variable is Nil, or have been released/auto released.
Here is the Stack trace, from the debugger:
Can anyone shed any light on this? Even some pointers on how i might investigate this better. it might not even be function specific.
I probobly should mention this function is performed on a separate thread.
Check the documentation for that returns your myPageRef and see if you have to retain it.
At the time of writing the question, UIGraphicsGetImageFromCurrentImageContext and such functions where not thread safe, this was my issue. As of iOs 4.1+ many UI function are now thread safe.

NSZombieEnabled breaking working code?

I have the following method in UIImageManipulation.m:
+(UIImage *)scaleImage:(UIImage *)source toSize:(CGSize)size
{
UIImage *scaledImage = nil;
if (source != nil)
{
UIGraphicsBeginImageContext(size);
[source drawInRect:CGRectMake(0, 0, size.width, size.height)];
scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return scaledImage;
}
I am calling it in a different view with:
imageFromFile = [UIImageManipulator scaleImage:imageFromFile toSize:imageView.frame.size];
(imageView is a UIImageView allocated earlier)
This is working great in my code. I resizes the image perfectly, and throws zero errors. I also don't have anything pop up under build -> analyze. But the second I turn on NSZombieEnabled to debug a different EXC_BAD_ACCESS issue, the code breaks. Every single time. I can turn NSZombieEnabled off, code runs great. I turn it on, and boom. Broken. I comment out the call, and it works again. Every single time, it gives me an error in the console: -[UIImage release]: message sent to deallocated instance 0x3b1d600. This error doesn't appear if `NSZombieEnabled is turned off.
Any ideas?
--EDIT--
Ok, This is killing me. I have stuck breakpoints everywhere I can, and I still cannot get a hold of this thing. Here is the full code when I call the scaleImage method:
-(void)setupImageButton
{
UIImage *imageFromFile;
if (object.imageAttribute == nil) {
imageFromFile = [UIImage imageNamed:#"no-image.png"];
} else {
imageFromFile = object.imageAttribute;
}
UIImage *scaledImage = [UIImageManipulator scaleImage:imageFromFile toSize:imageButton.frame.size];
UIImage *roundedImage = [UIImageManipulator makeRoundCornerImage:scaledImage :10 :10 withBorder:YES];
[imageButton setBackgroundImage:roundedImage forState:UIControlStateNormal];
}
The other UIImageManipulator method (makeRoundCornerImage) shouldn't be causing the error, but just in case I'm overlooking something, I threw the entire file up on github here.
It's something about this method though. Has to be. If I comment it out, it works great. If I leave it in, Error. But it doesn't throw errors with NSZombieEnabled turned off ever.
The purpose of NSZombieEnabled is to detect messages that get sent to objects after they've been deallocated. The console error you're seeing is NSZombieEnabled telling you that a release message is being sent to a deallocated instance of UIImage. Usually a bug like this is the result of too many calls to release, or not enough calls to retain.
In this case, your scaleImage:toSize: method returns an autoreleased UIImage. The error message you're getting from NSZombieEnabled suggests that you may be releasing this object after it gets returned. This would explain your bug. When your autorelease pool drains it would try to release an object that's already been deallocated.
You're passing imageFromFile to scaleImage:toSize:, and then reassigning that same variable to the return value. There's nothing wrong with this idiom per se, but does require some extra care to avoid memory bugs like this one. You're overwriting your reference to the original object, so you either have to make sure it's autoreleased before the assignment, or save a separate reference that you can manually release after the assignment. Otherwise your original object will leak.
The error was due to a release going on in the makeRoundedCornerImage method from UIImageManipulator. Still not sure why it wasn't getting picked up without NSZombieEnabled turned on, but that's what it was.
You can find the offending line in the Gist I posted in the original question: Line 74.