Memory usage is growing with CTFontCreateWithName and CTFramesetterRef

I am writing an iOS program that uses custom fonts (CTFontManagerRegisterFontsForURL). I load the font, add it as a string attribute, create a frassetter, then a frame and draw it into context. I release everything that I use. Tools do not notice a leak, but:

The memory used by applications grows and does not decrease when using this function. Holding my font, I leave function 2.

Here is the code:

CFMutableAttributedStringRef attributedStringRef = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); CFAttributedStringBeginEditing(attributedStringRef); CFAttributedStringReplaceString(attributedStringRef, CFRangeMake(0, 0), (CFStringRef)label.text); font = CTFontCreateWithName((CFStringRef)label.fontName, label.fontHeight, NULL); 

save font: 1

 CFAttributedStringSetAttribute(attributedStringRef, CFRangeMake(0, label.text.length), kCTFontAttributeName, font); CFAttributedStringEndEditing(attributedStringRef); 

save font size: 2

 CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path, NULL, rect); CFRelease(font); 

save font: 1

 CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(attributedStringRef); 

save font: 3

 CFRelease(attributedStringRef); CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL); 

save font: 5

 CFRelease(frameSetter); 

save font size: 4

 CTFrameDraw(frame, ctx); CFRelease(frame); 

save font size: 2

 CGPathRelease(path); 

Is there any kind of cache? I really need to immediately clear the memory used by this font.

PS: I used CFGetRetainCount to get the font hold value.

Thanks!

+5
source share
4 answers

keepCount is useless. Do not call it.

If your applicationโ€™s memory grows repeatedly, use Heapshot Analysis to find out what memory consumption is. Leaks only report objects that are no longer available โ€” objects whose address is not displayed in any active memory areas โ€” and thus, leaks will not find many kinds of memory.

This may be the case of a write-only cache; that is, somewhere proactively caching stuff somewhere, but your code is written in such a way that cached copies are never retrieved. Without additional information - the results of the Heapshot Analysis for beginners are hard to say.


I followed your tutorial and it confirms that the constant heap of growth is due to the line "CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString ((CFAttributedStringRef) string);". OK - you have confirmed that a leak occurs and where it is allocated, but not where additional retention occurs. To do this, enable the "Record link count" in the "Distributions" tool and repeat the test. This will allow you to check the backtraces of each save / release call on the offending object. There will be additional retention; a keep unbalanced release.

I guess the context is somehow hanging on it.

(I already analyzed the memory and saw that it was occupied by this object, so I checked the number of deductions.

The absolute value of saving an object is useless. The fact that it is still in memory means that it is excessively held, and the counter itself cannot really tell you anything else, except when you also have a complete return line for each individual call (and release) of the object that the Tools gives you.

+4
source

Ben, I did a deep dive into impl with a debugger device and iPhone 4, and it looks like the root of the problem is actually implemented in the implementation of CFMutableAttributedString. It seems that what happens is that any object passed to the mutable attribute string using the CFAttributedStringSetAttribute () or CFAttributedStringSetAttributes () methods will leak (because the ref will increase, but not decrease). You saw it with the name kCTFontAttributeName, but I tested it, and the same problem is found with the value kCTForegroundColorAttributeName or kCTParagraphStyleAttributeName. For example, I studied the memory used for a paragraph style object created with CTParagraphStyleCreate () and passed to attr str like this:

 CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphSettings, 1); CFRange textRange = CFRangeMake(0, [self length]); CFAttributedStringSetAttribute(mAttributedString, textRange, kCTParagraphStyleAttributeName, paragraphStyle); CFRelease(paragraphStyle); 

This paragraphStyle object will be saved internally with attr str, but then when it is time to drop the last ref to attr str via:

 CFRelease(attrString); 

The above should have cast the final ref down to the paragraphStyle object, but that is not the case. I can only come to one conclusion, this is a mistake in Apple's implementation of the modified attribute string. Please also note that I tried using CFAttributedStringRemoveAttribute () and CFAttributedStringSetAttributes () with a fake value, and clearOtherAttributes is set to TRUE, but nothing works to make the object drop references to property objects that it has.

Update: after some additional testing today, I found that this is the minimum application code needed to reproduce the leak in a very simple way. This avoids turning the text into a context, so this may not be a problem with the context preserving the font ref or something like that. You only need these 2 functions in the application delegation example:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; [self.timer invalidate]; self.timer = [NSTimer timerWithTimeInterval: 0.5 target: self selector: @selector(timerCallback:) userInfo: NULL repeats: TRUE]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode: NSDefaultRunLoopMode]; return YES; } // This callback is invoked onver and over on an interval. The goal of this function is to demonstrate // a memory leak in CoreText. When a font is set with CFAttributedStringSetAttribute() and then // the mutable string is copied by CTFramesetterCreateWithAttributedString(), the memory associated // with the font ref is leaked. - (void) timerCallback:(NSTimer*)timer { CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); CFStringRef cfStr = (CFStringRef)@"a"; CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), cfStr); CFRange range = CFRangeMake(0, 1); CTFontRef plainFontRef = CTFontCreateWithName((CFStringRef)@"Helvetica", 12, nil); // plainFontRef retain count incremented from 1 to 2 CFAttributedStringSetAttribute(attrString, range, kCTFontAttributeName, plainFontRef); // plainFontRef retain count incremented from 2 to 4. Note that in order to see // a leak this CTFramesetterCreateWithAttributedString() must be invoked. If // the creation of a framesetter is commented out, then the font inside the // attr string would be dellocated properly. So, this is likely a bug in the // implementation of CTFramesetterCreateWithAttributedString() in how it copies // properties from the mutable attr string. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString); // plainFontRef retain count decremented from 4 to 3 (note that it should have been decremented by 2) CFRelease(framesetter); // retain count is 1 at this point, so attrString is deallocated. Note that this should // drop the retain count of the font ref but it does not do that. CFRelease(attrString); // The retain count here should be 1 and this invocation should drop the last ref. // But the retain count for plainFontRef is 3 at this point so the font leaks. CFRelease(plainFontRef); return; } 

I tested this in a simulator (iOS 5 and 6) and on a device with iOS 5.1, and I see a leak in all cases. Can someone with iOS 6 or newer try this and see if there is a leak, the key is that the number of CTFont objects increases with the increase of the leak profile or distribution profile.

+2
source

Have you run your code in the Tool (you have a profile).

Holding an objectโ€™s count does not increase your memory usage, it simply indicates that more of the object is interested in this particular object.
If he would be released if it is assumed that you do not care about the actual value of the amount of the deductions, this is often not what you expect, and Apple recommends not to use retainCount as a debugging tool because of this. This may give you a general idea of โ€‹โ€‹how much your object is in demand (saved by others), but what it is.

In the tool, you have a call to the Leaks tool, which is good at detecting a memory leak.

I often saw that the object has a hold count of 2 when I expected them to have a count of 1, but they were freed where they should have been.
If you have a score of 5 points at the time when you think that it should be released, this may be a sign that something is wrong, but not even a guarantee.

0
source

this is now fixed until you release CTFramesetterRef .

(... and make sure you reinstall your application back to your device before restarting Instruments after changing the code!).

0
source

Source: https://habr.com/ru/post/1386136/


All Articles