Бен, я немного углубился в отсылку к отладчику с устройством iPhone 4, и похоже, что корень проблемы на самом деле в реализации CFMutableAttributedString. Похоже, что происходит, что любой объект, переданный в изменяемую атрибутивную строку с использованием методов CFAttributedStringSetAttribute () или CFAttributedStringSetAttributes (), будет иметь утечку (потому что ссылка будет увеличиваться, но не уменьшаться). Вы видели это с kCTFontAttributeName, но я проверил это, и та же проблема обнаруживается со значением kCTForegroundColorAttributeName или kCTParagraphStyleAttributeName. Например, я проверил память, используемую для объекта стиля абзаца, созданного с помощью CTParagraphStyleCreate () и переданного в строку attr следующим образом:
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphSettings, 1);
CFRange textRange = CFRangeMake(0, [self length]);
CFAttributedStringSetAttribute(mAttributedString, textRange, kCTParagraphStyleAttributeName, paragraphStyle);
CFRelease(paragraphStyle);
Этот объект paraStyle будет внутренне сохранен attr str, но затем, когда придет время сбросить последний ref в attr str через:
CFRelease(attrString);
Вышеприведенное должно было бы сбросить окончательную ссылку на объект paraStyle, но это не так. Я могу только прийти к одному выводу, это ошибка в реализации Apple изменяемой атрибутивной строки. Также обратите внимание, что я пробовал CFAttributedStringRemoveAttribute () и CFAttributedStringSetAttributes () со фальшивым значением и clearOtherAttributes, установленным в TRUE, но, похоже, ничего не работает, чтобы заставить объект сбросить ссылки на объекты свойств, которые он содержит.
Обновление: после некоторого дополнительного тестирования сегодня я обнаружил, что это минимальный код приложения, необходимый для очень простого воспроизведения утечки. Это позволяет избежать рендеринга текста в контексте, поэтому не может быть проблем с сохранением шрифта ref или чего-то еще. Вам нужны только эти 2 функции в примере делегата приложения:
- (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, и я вижу утечку во всех случаях. Может кто-то с iOS 6 или новее попробовать это и посмотреть, если утечка также появляется там, ключ в том, что число объектов CTFont продолжает увеличиваться либо с профилем утечек, либо с профилем распределений.