2011-12-13 72 views
4

我正在寫一個使用自定義字體(CTFontManagerRegisterFontsForURL)的IOS程序。我加載字體,將其添加爲字符串屬性,創建一個framesetter,然後創建一個框架,然後將其繪製到上下文中。 我釋放我使用的一切。儀器不會注意到泄漏,但是:內存使用增長與CTFontCreateWithName和CTFramesetterRef

使用此功能時,應用程序使用的內存會增大並且不縮小。 當我離開函數時,我的字體的保留計數是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); 

有沒有某種緩存?我真的需要立即刷新該字體使用的內存。

P.S:我用CFGetRetainCount來獲取字體的保留數。

謝謝!

回答

0

你是否在Instrument中運行你的代碼(你有沒有分析它)?

對象的保留計數不會增加您的內存使用量,它只是指出更多對象對該特定對象感興趣。
如果它假設是你不關心保留計數的實際值,它通常不是你所期待的,並且Apple建議不要使用retainCount作爲調試工具。它可以讓你大致瞭解你的對象有多少需求(由其他人保留),但就是這樣。

在儀器中,你有一個工具調用「泄漏」,很好地發現內存泄漏。

我經常看到對象的保留數爲2,當時我期待他們的保留數爲1,但是他們被解除分配他們應該在的地方。
如果在您認爲應該取消分配之前您的保留計數爲5,那可能表明存在某些錯誤,但不是擔保。

+0

我使用Memory Allocation工具和Memory Leaks工具在Instruments中運行它。我沒有得到任何內存泄漏,但是使用分配工具,我可以看到我的字體仍然被分配(並且我的應用程序內存使用率更高)。我的猜測是有些東西仍然保留着它,但是什麼?爲什麼?以及我如何強制釋放?你的代碼中的 – Ben

+0

我會說'ctx'正在保留它以供個人使用。你在內存中只有一個,或者當你運行你的代碼時,它們正在堆積起來? –

+0

ctx如果以這種方式獲得:UIGraphicsBeginImageContext,然後UIGraphicsGetCurrentContext,然後我調用我的方法,然後我UIGraphicsGetImageFromCurrentImageContext和UIGraphicsEndImageContext。 – Ben

4

retainCount無用。別叫它。

如果您的應用程序內存以可重複的方式增長,請使用Heapshot Analysis來確定耗費內存的內容。泄漏僅報告不再可到達的對象 - 地址未出現在內存的任何活動區域中的對象 - 因此泄漏不會找到多種內存增加。

這可能是隻寫緩存的情況;即在某處正在主動緩存東西,但是您的代碼被寫入,從而永遠不會檢索緩存的副本。沒有額外的信息 - 對於初學者來說,堆積分析的結果 - 很難說。


我跟着你的教程,和它確認永久堆 增長的原因是行 「CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)字符串); 」。 確定 - 您已確認什麼正在泄漏以及分配的位置,但未分配額外保留的位置。爲此,打開「分配」工具中的「記錄引用計數」並重新運行測試。這將允許您檢查違規對象上的每個保留/釋放調用的回溯。那裏會有額外的保留;保留不平衡的釋放。

我猜上下文以某種方式掛在它上面。

(我已經分析了記憶,發現它是由 這些對象佔用的,這就是爲什麼我檢查的保留計數。

絕對保留對象的數量是沒有用的。這是仍然在內存中意味着它被過度保留,並且保留計數本身並不能真正告訴你什麼,除非你還完全回溯了對象上的每一次保留(和釋放)調用,哪些儀器給你。

+0

我跟着你的教程,它確認永久堆增長是由於「CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);」行。 (我已經分析了內存,並看到它被這個對象佔用,這就是爲什麼我檢查了保留數。 – Ben

0

本,我用調試器和iPhone做了一些深入的介紹, 4設備,它看起來像問題的根源實際上在CFMutableAttributedString實現中。它看起來像是使用CFAttributedStringSetAttribute()或CFAttributedStringSetAttributes()方法傳入可變屬性字符串的任何對象都會泄漏(因爲ref會遞增但不遞減)。你用kCTFontAttributeName看到它,但我測試了它,同樣的問題也出現了kCTForegroundColorAttributeName或kCTParagraphStyleAttributeName值。例如,我檢查用於通過CTParagraphStyleCreate(創建的段落樣式對象),並傳遞到ATTR STR像這樣的存儲器:

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

這paragraphStyle對象將被由ATTR STR內部保留,但然後當它待到最後裁判拖放到ATTR STR通過:

CFRelease(attrString); 

上面應該有最後的裁判降到paragraphStyle對象,但事實並非如此。我只能得出一個結論,這是蘋果執行可變屬性字符串時的一個錯誤。還要注意,我用虛假的值嘗試了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對象的數量會隨着泄漏配置文件或分配配置文件而不斷增加。