0

我有一种方法可以计算文本行的高度。但问题是它使用 2 项泄漏内存:CTFrame 和 Malloc(48 字节)。

以下是该方法的核心:

(void)calculatePageHeight {
    __weak UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.view.bounds];

    NSString *sampleText = @"The CTFont opaque type represents a Core Text font object. Font objects represent fonts to an application, providing access to characteristics of the font, such as point size, transform matrix, and other attributes. Fonts provide assistance in laying out glyphs relative to one another and are used to establish the current font when drawing in a graphics context.";
    NSRange contentRange = NSRangeFromString(sampleText);
    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:sampleText attributes:self.textAttributes];

    CFAttributedStringRef attributeRef = (__bridge CFAttributedStringRef)attributedString;

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributeRef);

    CGPathRef myPath = [path CGPath];
    CFRange myRange = CFRangeMake(contentRange.location, contentRange.length);

    CTFrameRef contentFrame = CTFramesetterCreateFrame(framesetter, myRange , myPath, nil);
    CFArrayRef lines = CTFrameGetLines(CFRetain(contentFrame));
    NSInteger lineCount = CFArrayGetCount(lines);
    CGPoint *origins = calloc(lineCount, sizeof(CGPoint));

    CTFrameGetLineOrigins(contentFrame, CFRangeMake(0, 0), origins);
    CGFloat lineSpacing = 0;

    for (NSInteger index = 0; index < lineCount; index ++) {
        CTLineRef line = CFArrayGetValueAtIndex(lines, index);
        CGFloat ascent;
        CGFloat descent;

        CTLineGetTypographicBounds(line, &ascent, &descent, nil);
        NSLog(@"line height: %f", ascent + (descent * 2));

        lineSpacing = ascent + (descent * 2);
    }
    free(origins);
    CFRelease(lines);
    //free(contentFrame);

    NSLog(@"line spacing: %f", lineSpacing);

    NSInteger numberOfLine = TEXT_PAGE_HEIGHT / (lineSpacing);

    CGFloat pageHeight = numberOfLine * (lineSpacing);
    self.pageHeight = pageHeight;

    CGPathRelease(myPath);
    CFRelease(framesetter);
}

如果我取消注释该行,CTFrame 将出现,但会出现警告:

Passing CTFrameRef (aka const struct_CTFrame *) to parameter of type "void *' discards qualifiers)

free(contentFrame);

那么泄漏将只有一个用于 Malloc。仪器工具让我知道这行代码导致泄漏。

CTFrameRef contentFrame = CTFramesetterCreateFrame(framesetter, myRange , myPath, nil);

任何人都可以帮助我解释这一点,我无法解释为什么 Malloc 会泄漏。以及如何解决这个问题,如何释放 CTFrame 对象?

我研究了很多,但找不到解决方案。

4

2 回答 2

3

使用CFRelease而不是free,文档没有提到使用CFRelease来释放CT*对象,但它确实有效。

于 2012-05-11T03:56:11.660 回答
0

问题出在以下几行:

CTFrameRef contentFrame = CTFramesetterCreateFrame(framesetter, myRange , myPath, nil);
CFArrayRef lines = CTFrameGetLines(CFRetain(contentFrame));

在第一行中 contentFrame 的保留计数器为 1,在第二行中它增加到 2。

你不应该调用 CFRetain() ,当你完成使用它时,写:

CFRelease(contentFrame), contentFrame = NULL;

另外,请确保 CTFrame 不为 NULL。它可能尚未创建。而且......不要释放线条!你不为他们分配内存,你只是得到参考。

你的代码应该是这样的:

    (void)calculatePageHeight {
    //__weak UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.view.bounds];

    NSString *sampleText = @"The CTFont opaque type represents a Core Text font object. Font objects represent fonts to an application, providing access to characteristics of the font, such as point size, transform matrix, and other attributes. Fonts provide assistance in laying out glyphs relative to one another and are used to establish the current font when drawing in a graphics context.";
    NSRange contentRange = NSRangeFromString(sampleText);
    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:sampleText attributes:self.textAttributes];

    CFAttributedStringRef attributeRef = (__bridge CFAttributedStringRef)attributedString;

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributeRef);
    if(framesetter == NULL)
    {
      [attributedString release], attributedString = nil;
      return;
    }

    CGMutablePathRef myPath = CGPathCreateMutable();
    CGPathAddRect(myPath, self.view.bounds);

    CFRange myRange = CFRangeMake(contentRange.location, contentRange.length);

    CTFrameRef contentFrame = CTFramesetterCreateFrame(framesetter, myRange , myPath, nil);
    if(contentFrame == NULL)
    {
      CFRelease(framesetter), framesetter = NULL;
      [attributedString release], attributedString = nil;
      return;
    }
    CFArrayRef lines = CTFrameGetLines(contentFrame);
    NSInteger lineCount = CFArrayGetCount(lines);
    CGPoint *origins = (CGPoint *)malloc(lineCount * sizeof(CGPoint));

    CTFrameGetLineOrigins(contentFrame, CFRangeMake(0, 0), origins);
    CGFloat lineSpacing = 0.0f;

    for (NSInteger index = 0; index < lineCount; index ++) {
        CTLineRef line = CFArrayGetValueAtIndex(lines, index);
        CGFloat ascent;
        CGFloat descent;

        CTLineGetTypographicBounds(line, &ascent, &descent, nil);
        NSLog(@"line height: %f", ascent + (fabs(descent) * 2));

        lineSpacing = ascent + (fabs(descent) * 2);
    }
    free(origins);
    //CFRelease(lines);
    //free(contentFrame);

    NSLog(@"line spacing: %f", lineSpacing);

    NSInteger numberOfLine = TEXT_PAGE_HEIGHT / (lineSpacing);

    CGFloat pageHeight = numberOfLine * (lineSpacing);
    self.pageHeight = pageHeight;

    CGPathRelease(myPath), myPath = NULL;
    CFRelease(framesetter), framesetter = NULL;
    CFRelease(contentFrame), contentFrame = NULL;
    [attributedString release], attributedString = nil;
}
于 2012-12-19T18:11:36.157 回答