在iPhone上使用带有CoreText的某些字体的Unicode文本时内存泄漏

时间:2010-11-10 08:50:09

标签: iphone cocoa-touch memory-leaks core-text

我在iPhone上遇到一些有趣的CoreText问题,在某些情况下导致内存泄漏。

我在文档和互联网上随处可见,似乎没有人遇到同样的问题。但是,情况可能很特殊(见下文)。

无论如何,在经过大量缩小之后,我设法得到了这个重复案例:

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];
}

根据字体和文本的不同,这可能会或可能不会根据泄漏工具泄漏 - 各种/大量的东西,但绝对看似字体和字体描述符相关。

这些可能导致如下:

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?

显然我注释掉了以便使用Leaks工具留下一个leakTest调用进行测试!

因此,具有不寻常但完全合法的unicode字符的字符串会导致一种字体泄漏但不会泄漏另一种字体。

我的代码中有什么问题,我恰好用Arial“侥幸成功”吗?我想知道是否可能存在导致明显泄漏的某种字体缓存,但操作系统或其他任何实际上都处理它没问题。

希望你能帮助别人!

2 个答案:

答案 0 :(得分:0)

虽然泄露的对象是CTFont,但我敢打赌NSMutableAttributedString正在泄漏。尝试添加一个@"bogus"属性,例如一个空的NSData对象,看看它是否也被泄露了。

答案 1 :(得分:0)

查看此代码(没有内存泄漏),我使用不同的字体和unicode文本。 当我使用(CFMutableAttributedStringRef)字符串counstruction时,存在内存泄漏。

我认为您必须使用CFAttributedStringCreateMutable()来消除泄漏。

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);
    }
}