Memory usage grows with CTFontCreateWithName and CTFramesetterRef

I’m writing an IOS program which uses custom fonts (CTFontManagerRegisterFontsForURL). I load the font, add it as a string attribute, create a framesetter, then a frame, and draw it to a context.
I release everything i use. Instruments doesn’t notice a leak but :

The memory used by the applications grows and doesn’t shrink when using this function.
The retain count of my font is 2 when i leave the function.

  • CoreText with multicolumn with image?
  • Issue with UILabel center text alignment at different font sizes in Xcode Swift
  • CATextLayer + NSAttributtedString + CTParagraphStyleRef
  • Cannot use custom font in iPhone app
  • CTFramesetterSuggestFrameSizeWithConstraints with clipping attributes returning zero height
  • -fontWithSize returning different font object
  • 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);
    

    retain count of the font : 1

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

    retain count of the font : 2

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

    retain count of the font : 1

    CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(attributedStringRef); 
    

    retain count of the font : 3

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

    retain count of the font : 5

    CFRelease(frameSetter);
    

    retain count of the font : 4

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

    retain count of the font : 2

    CGPathRelease(path);
    

    Is there some sort of cache ? I really need to flush the memory used by this font immediately.

    P.S : I used CFGetRetainCount to get the retain count of the font.

    Thanks !

    3 Solutions Collect From Internet About “Memory usage grows with CTFontCreateWithName and CTFramesetterRef”

    retainCount is useless. Don’t call it.

    If your app’s memory is growing in a repeatable fashion, use Heapshot Analysis to figure out what is consuming memory. Leaks only reports objects that are no longer reachable — objects whose address does not appear in any active regions of memory — and, thus, leaks will not find many kinds of memory accretion.

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


    I followed your tutorial, and it confirms that the permanent heap
    growth is due to the line “CTFramesetterRef frameSetter =
    CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
    “.
    OK — you’ve confirmed what is leaking and where it is allocated, but not where the extra retain comes from. To that, turn on “Record reference counts” in the Allocations instrument and re-run the test. This will allow you to inspect the backtraces of every retain/release call on the offending object. There will be an extra retain in there; a retain not balanced by a release.

    I’m guessing the context is somehow hanging on to it.

    (I had already analyzed the memory and saw that it was occupied by
    this object, that’s why i checked retain count.

    The absolute retain count of an object is useless. That it is still in memory means that it is over-retained and the retain count, itself, can’t really tell you anything more unless you also have the full backtrace of every single retain (and release) call on the object, which Instruments gives you.

    Have you run your code in Instrument (have you profile it).

    The retain count of an object don’t grows your memory usage it’s just stating that more object are interested in that particular object.
    If it get deallocated when it’s suppose to be you don’t care about the actual value of the retain count, often it’s not what you are expecting, and Apple advise to not use the retainCount as a debugging tool because of that. It can give you a general idea of how much your object is in demand (retain by others) but that’s it.

    In instrument you have a tool call “Leaks” that is good at finding memory leak.

    I often have seen object have a retain count of 2, when I was expecting them to have a retain count of 1, but they were deallocated where they were suppose to be.
    If you have a retain count of 5 at the point just before you think it should get deallocated, that could be an indication that something is wrong, but not even a guaranty.

    Ben, I did some deep diving into the impl with the debugger with and iPhone 4 device and it looks like the root of the problem is actually in the CFMutableAttributedString implementation. It looks like what is going on is that any object passed into an mutable attributed string using the CFAttributedStringSetAttribute() or CFAttributedStringSetAttributes() methods will leak (because the ref will be incremented but not decremented). You were seeing it with a kCTFontAttributeName, but I tested it and the same problem shows up with a kCTForegroundColorAttributeName or kCTParagraphStyleAttributeName value as well. For example, I examined the memory used for a paragraph style object created via CTParagraphStyleCreate() and passed into the attr str like so:

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

    This paragraphStyle object would be preserved internally by the attr str, but then when it came time to drop the last ref to the attr str via:

    CFRelease(attrString);
    

    The above should have dropped the final ref to the paragraphStyle object, but it does not. I can only come to one conclusion, this is a bug in Apple’s implementation of the mutable attributed string. Note also that I tried CFAttributedStringRemoveAttribute() and CFAttributedStringSetAttributes() with a phony value and the clearOtherAttributes set to TRUE but nothing seems to work to force the object to drop the refs to the property objects it holds.

    Update: after some additional testing today, I found that this is the minimal app code needed to reproduce the leak in a very simple way. This avoids ever rendering the text into a context, so it cannot be a problem with the context saving the font ref or something. You only need these 2 functions in an app delegate 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 have tested this in the simulator (iOS 5 and 6) and on a device with iOS 5.1 and I see the leak in all cases. Could someone with iOS 6 or newer try this out and see if the leak appears there also, the key is that the number of CTFont objects keeps increasing either with the leaks profile or the allocations profile.