内存使用增长与CTFontCreateWithName和CTFramesetterRef

我正在写一个使用自定义字体(CTFontManagerRegisterFontsForURL)的IOS程序。 我加载字体,将其添加为一个string属性,创build一个framesetter,然后一个框架,并绘制到上下文。 我释放我使用的一切。 仪器不会注意到泄漏,但是:

使用此function时,应用程序所使用的内存会增长并且不会收缩。 当我离开函数时,我的字体的保留计数是2。

这里是代码:

CFMutableAttributedStringRef attributedStringRef = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); CFAttributedStringBeginEditing(attributedStringRef); CFAttributedStringReplaceString(attributedStringRef, CFRangeMake(0, 0), (CFStringRef)label.text); font = CTFontCreateWithName((CFStringRef)label.fontName, label.fontHeight, NULL); 

保留字体的数量:1

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

保留字体的数量:2

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

保留字体的数量:1

 CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(attributedStringRef); 

保留字体的数量:3

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

保留字体的数量:5

 CFRelease(frameSetter); 

保留字体的数量:4

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

保留字体的数量:2

 CGPathRelease(path); 

有没有某种caching? 我真的需要立即刷新这个字体使用的内存。

PS:我用CFGetRetainCount来获取字体的保留数。

谢谢 !

retainCount是没用的。 别叫它。

如果您的应用程序的内存以可重复的方式增长,请使用Heapshot Analysis找出消耗内存的内容。 泄漏仅报告不再可到达的对象 – 地址未出现在任何活动的内存区域中的对象 – 因此,泄漏将不会find多种内存增加。

这可能是只写高速caching的情况; 即在某个地方主动caching的东西,但是你的代码是这样写的,以避免caching的副本检索。 没有额外的信息 – 对于初学者来说,堆积分析的结果 – 很难说。


我跟着你的教程,它确认永久堆增长是由于“CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string)”。 好的 – 你已经确认了什么是泄漏和分配的位置,但没有额外的保留来自哪里。 为此,打开“分配”工具中的“logging引用计数”并重新运行testing。 这将允许您检查违规对象上的每个保留/释放调用的回溯。 那里会有额外的保留; 保留不平衡的释放。

我猜测上下文是以某种方式挂在它上面的。

(我已经分析了内存,看到它被这个对象占用了,所以我查了一下保留数。

一个对象的绝对保留数是无用的。 它仍然在内存中意味着它被过度保留,并且保留计数本身不能真正告诉你什么,除非你对对象上的每一个保留(和释放)调用都有完整的回溯,给你。

你有没有在Instrument中运行你的代码(你有没有分析过它)?

一个对象的保留计数不会增加你的内存使用量,它只是说明有更多的对象对这个特定的对象感兴趣。
如果它被假定为被释放,那么你不关心保留计数的实际值,通常这不是你所期望的,Applebuild议不要使用retainCount作为debugging工具。 它可以给你一个大致的想法,你的对象有多less需求(由其他人保留),但就是这样。

在仪器中你有一个工具调用“泄漏”,很好地发现内存泄漏。

我经常看到对象的保留数为2,当我期待他们的保留数为1时,但是他们被解除了分配。
如果在你认为它应该被释放之前,你的保留数是5,那可能表明有什么错误,但不是保证。

本,我用debugging器和iPhone 4设备做了一些深入的impl,看起来问题的根源实际上在CFMutableAttributedString实现中。 看起来像是使用CFAttributedStringSetAttribute()或CFAttributedStringSetAttributes()方法传递给可变属性string的任何对象将泄漏(因为ref会递增但不递减)。 你用kCTFontAttributeName看到它,但是我testing了它,同样的问题也出现了kCTForegroundColorAttributeName或者kCTParagraphStyleAttributeName值。 例如,我检查了用于通过CTParagraphStyleCreate()创build的段落样式对象的内存,并像下面这样传递给attr str:

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

这段落风格的对象会被attr str内部保存下来,但是当它通过下面的方式把最后一个引用放到attr str时:

 CFRelease(attrString); 

上面应该已经把最后的ref放到了paragraphStyle对象上,但是没有。 我只能得出一个结论,这是苹果执行可变属性string的一个错误。 还要注意,我用虚假的值尝试了CFAttributedStringRemoveAttribute()和CFAttributedStringSetAttributes(),并将clearOtherAttributes设置为TRUE,但似乎没有任何东西可以强制对象将引用放到它所拥有的属性对象中。

更新:今天经过一些额外的testing后,我发现这是以非常简单的方式重现泄漏所需的最小应用程序代码。 这避免了将文本渲染到上下文中,所以在保存字体ref的上下文中不会有问题。 您只需要在应用程序委托示例中使用这两个函数:

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

我已经在模拟器(iOS 5和6)以及使用iOS 5.1的设备上testing了这一点,并且在所有情况下都看到了泄漏。 有人可以使用iOS 6或更高版本来试用这个,看看是否泄漏出现在那里,关键是CTFont对象的数量不断增加,无论是泄漏configuration文件或分配configuration文件。