Jon Burnable
Jon Burnable

Reputation: 31

Memory leak when using Unicode text with certain fonts with CoreText on iPhone

I'm having some interesting issues with CoreText on iPhone, causing memory leaks in certain circumstances.

I've looked everywhere in the documentation and across the internet and no-one seems to be getting the same issue. However, the circumstances are perhaps special (see below).

Anyway, after a lot of narrowing down, I managed to get this repro case:

void leakTest(NSString* fontname, NSString* text)
{
    NSDictionary* descriptorAttr = [NSDictionary dictionaryWithObjectsAndKeys:
        fontname, (const NSString*)kCTFontFamilyNameAttribute, nil];

    CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes((CFDictionaryRef)descriptorAttr);
    CTFontRef           font       = CTFontCreateWithFontDescriptor(descriptor, 0, nil);

    NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
        (id)font, (const NSString*)kCTFontAttributeName, nil];

    CFRelease(descriptor);
    CFRelease(font);

    NSMutableAttributedString* string = [[NSMutableAttributedString alloc] initWithString:text];
    [string setAttributes:dict range:NSMakeRange(0, string.length)];

    CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((CFMutableAttributedStringRef)string);

    CFRelease(typesetter);
    [string release];
}

Depending on the font and text, this may or may not leak according to the Leaks tool - various/numerous things but definitely seeming font and font descriptor related.

These can be caused as follows:

const char* unicodeText = "Ernle\310\235e"; // "Ernleȝe" if your editor groks unicode 

NSString* textLeaky  = [NSString stringWithUTF8String:unicodeText];
NSString* textNormal = @"Hello World";

leakTest(@"Courier", textNormal);   // This doesn't leak
leakTest(@"Courier", textLeaky);    // This does leak
leakTest(@"Arial",   textLeaky);    // This doesn't leak with this font?

Obviously I commented out so as to leave one leakTest call to test with the Leaks tool!

So, the string with an unusual but perfectly legal unicode character in causes a leak with one font but not the other.

Is there something wrong in my code and I happen to be "getting away with it" with Arial? I wondered whether there may be some kind of font caching that was causing the apparent leak but the OS or whatever is actually all handling it okay.

Hope you can help folks!

Upvotes: 1

Views: 1632

Answers (2)

Zeine
Zeine

Reputation: 21

look at this code (no memory leaks) and I use different fonts and unicode text. There were memory leaks when I use (CFMutableAttributedStringRef)string counstruction.

I think you have to use CFAttributedStringCreateMutable() to remove leaks.

CTTextAlignment alignment = kCTLeftTextAlignment;
CTLineBreakMode breakMode = kCTLineBreakByTruncatingTail;
CGFloat paragraphSpacing = 0;
CGFloat maximumLineSpacing = 0;

CTParagraphStyleSetting paragraphStyleSetting[] = {
    {kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment},
    {kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &breakMode},
    {kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), &paragraphSpacing},
    {kCTParagraphStyleSpecifierMaximumLineSpacing, sizeof(CGFloat), &maximumLineSpacing}
};

CTParagraphStyleRef paragraphStyleRef = CTParagraphStyleCreate(paragraphStyleSetting, sizeof(paragraphStyleSetting) / sizeof(paragraphStyleSetting[0]));

CFMutableAttributedStringRef attributedStringRef = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
if (attributedStringRef) {
    CFAttributedStringReplaceString(attributedStringRef, CFRangeMake(0, 0), (CFStringRef)aText);
    CFAttributedStringSetAttribute(attributedStringRef, CFRangeMake(0, aText.length), kCTFontAttributeName, aFontRef);
    CFAttributedStringSetAttribute(attributedStringRef, CFRangeMake(0, aText.length), kCTForegroundColorAttributeName, aColorRef);
    CFAttributedStringSetAttribute(attributedStringRef, CFRangeMake(0, aText.length), kCTParagraphStyleAttributeName, paragraphStyleRef);

    CFRelease(paragraphStyleRef);

    CTTypesetterRef typesetterRef = CTTypesetterCreateWithAttributedString(attributedStringRef);
    //skip
    CFRelease(typesetterRef);

    CTFramesetterRef framesetterRef = CTFramesetterCreateWithAttributedString(attributedStringRef);
    CFRelease(attributedStringRef);

    if (framesetterRef) {
    //skip
        CFRelease(frameRef);
    }
}

Upvotes: 0

Ned Holbrook
Ned Holbrook

Reputation: 126

Although the object being leaked is a CTFont, I would bet NSMutableAttributedString is doing the leaking. Try adding a @"bogus" attribute with, say, an empty NSData object and see if it is also leaked.

Upvotes: 0

Related Questions