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

时间:2022-01-08 08:59:55

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

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

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!

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

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

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

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.

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

Hope you can help folks!

希望你能帮助大家!

2 个解决方案

#1


0  

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.

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

#2


0  

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.

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

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

我认为你必须使用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);
    }
}

#1


0  

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.

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

#2


0  

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.

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

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

我认为你必须使用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);
    }
}