I inspected my app with the allocation instrument and I discovered that this code down here cause me an allocation issue. The method returns the suggested height of a squared area filled with the passed attributed string; I need this in order to calculate how much space I need to draw that text and then generating book pages:
- (CGFloat)boundingHeightForWidth:(CGFloat)inWidth ForAttributedString:(NSAttributedString *)attributedString
{
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFMutableAttributedStringRef)attributedString);
CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, CGSizeMake(inWidth, 10000), NULL);
CFRelease(framesetter);
return suggestedSize.height ;
}
Since I am calling this method many and many times during the flow, I am wondering how this is causing up to 7MB of memory allocation.. I thought releasing the frame setter could be enough, am I wrong?
I did some detailed debugging WRT to this issue, you can find the results in my answer to this question. There are a couple of things you could try. 1, does you app do these allocations in a secondary thread, if yes then does moving them to the main thread make the lost memory go away? Two, you could hold on to the CTFramesetterRef and then invoke CTFramesetterSuggestFrameSizeWithConstraints over and over with the same framesetter. The leak appears to be in the CTFramesetterCreateWithAttributedString() call, so perhaps you could minimize the leak by not invoking that method so many times.
Related
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.
I have an iPhone app where I'm "adding" a lot of CGColors together by breaking them down into their components, averaging the components, and then making a new color with the new components. When I run this code, Instruments finds that I'm leaking lots of CGColors, and the app runs slowly.
I feel like I could solve the memory leak issue if there were a way to do what I'm doing without using CGColorCreate(colorspace, components) every time.
This is the code for the color "adding"
const CGFloat *cs=CGColorGetComponents(drawColor);
const CGFloat *csA=CGColorGetComponents(add->drawColor);
CGFloat r=(cs[0]*w+csA[0]*aW)/1;
CGFloat g=(cs[1]*w+csA[1]*aW)/1;
CGFloat b=(cs[2]*w+csA[2]*aW)/1;
CGFloat components[]={r, g, b, 1.f};
drawColor=CGColorCreate(CGColorSpaceCreateDeviceRGB(), components);
Any help would be really appreciated, even if the help is "add the colors less often." I'm sure I'm not the only person trying to modify CGColors.
EDIT: So, rob's comment put me on the right track, but I'm getting malloc double free errors because the method with the color adding is called multiple times before a new drawColor is assigned. Is there a way to check whether drawColor exists before I release it? Here is the new relevant code.
CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
CGColorRelease(drawColor);
drawColor=CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
Pretty sure you just need to CGColorRelease(drawColor) to prevent the leak. See how that helps your performance.
If you're leaking CGColor objects, the first step to solving your problem is to stop leaking them. You need to call CGColorRelease when you're done with a color object. For example, you are obviously leaking the drawColor object in your example code. You should be doing this:
CGColorRelease(drawColor);
drawColor=CGColorCreate(CGColorSpaceCreateDeviceRGB(), components);
to release the old object referenced by drawColor before you assign the new object to drawColor.
CGColor objects are immutable, so you won't be able to just modify your existing objects.
For example is the following code memory safe?
NSMutableAttributedString *str = ...;
CTFontRef aFont = CTFontCreateWithName((CFStringRef)fontName, size, NULL);
[str addAttribute:(NSString*)kCTFontAttributeName value:(id)aFont range:range];
CFRelease(aFont);
Also, is CTFontCreateWithName efficient to call multiple times or should some effort be made to cache CTFontRef's for the same font/size?
I believe it is safe to release the font object after adding it as an attribute. I have done so in my own Core Text code and never have any issues.
As for caching, it would make sense to keep a font object around if it will be used multiple times rather than releasing it and recreating it many times. Though, this is likely pre-optimisation, so I wouldn't make any conscious effort just yet. Profile it with your current code and decide whether or not the extra microseconds will be worth the work.
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.
I have a couple labels I am using as a HUD for the player during a game. I am updating these labels frequently, so that the player has up-to-date information. The problem is is that I've been using
uiLabel.text = [NSString stringWithFormat:#"%3.0f", value];
to pass the new value that the label should have. I have noticed, however, that I have something of a soft-memory leak here. As I'm doing this update multiple times a second and this creates a string that is set to autorelease, I'm ending up taking more memory than I need. And keeping it, as the view is not going away.
I also tried to alloc and release strings explicitly, such as:
NSString* value = [[NSString alloc] initWithFormat: #"%3.0f", value];
uiLabel.text = value;
[value release];
However, I find that this seems to cause the same thing, but faster, though I don't know why. In this situation I would have thought there should never be strings sitting around waiting to be released at all, since I'm so explicitly dismissing them.
Can anyone see what I'm doing here that I obviously am failing to see? Is there a better/more preferred way to handle this? Some cursory searching didn't turn up much for me.
You're not doing anything out of the ordinary. Even with:
uiLabel.text = [NSString stringWithFormat:#"%3.0f", value];
the autorelease pool gets drained every time your code returns control to the run loop (so at least as often as you see the UI updating). If you see growing memory allocations, you should look elsewhere.