5

I've reduced a leak issue to this easy to compile code which shows after CTFontCreateWithGraphicsFont use and release ct_font, an extra ref to cg_font will be left. Is this an internal Apple ref count issue or am I missing something around like having to double release cg_font or changing order of the releases? Thanks.

#include <stdio.h>
#include <stdlib.h>
#include <ApplicationServices/ApplicationServices.h>

int main(int argc, char **argv) {
    FILE *f = fopen("/Library/Fonts/Tahoma.ttf", "rb");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);
    
    char* font = (char*)malloc(fsize);
    fread(font, fsize, 1, f);
    fclose(f);
    
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, font, fsize, NULL);
    CGFontRef cg_font = CGFontCreateWithDataProvider(provider);
    CTFontRef ct_font = CTFontCreateWithGraphicsFont(cg_font, 36., NULL, NULL);
    CGDataProviderRelease(provider);

    //
    
    CFRelease(ct_font);
    CFRelease(cg_font);
    
    printf("ct_font: %d\ncg_font: %d\n", (int)CFGetRetainCount(ct_font), (int)CFGetRetainCount(cg_font));

    free(font);
    
    return 0;
}

Result after compile and run:

ct_font: -1

cg_font: 1

Community
  • 1
  • 1
Ebrahim Byagowi
  • 10,338
  • 4
  • 70
  • 81
  • 1
    Checking retain count is unreliable. Among other things, `CFGetRetainCount()` **can never (correctly) return 0**. There's no such thing as an object with a zero retain count. The object would be gone and the call would be invalid with undefined results. Try running your program under the Allocations and Zombies instruments to determine what's really happening. – Ken Thomases Nov 26 '16 at 21:13
  • Thanks. If I run `CGDataProviderRef` line to `CFRelease` in a 100 iteration loop, I'll get 370k persistent memory there, is there something similar to a GC cycle that I should wait for? – Ebrahim Byagowi Nov 26 '16 at 22:06
  • Well, you could wrap it in an `@autoreleasepool {}` block. That's Objective-C, but may be necessary in this case. Similarly, I'd be curious if this still happens in a call out from a run loop if you let execution return to the run loop. – Ken Thomases Nov 26 '16 at 22:46
  • 3
    Also, you should consider using `CTFontManagerCreateFontDescriptorFromData()` or `CTFontManagerCreateFontDescriptorsFromURL()` and then `CTFontCreateWithFontDescriptor()`, avoiding `CGFont` entirely. – Ken Thomases Nov 26 '16 at 22:52
  • Thanks, actually seems CTFontManagerCreateFontDescriptorFromData what was I was missing :) – Ebrahim Byagowi Nov 27 '16 at 05:23

1 Answers1

3

The retain of the CGFont happens in the TInMemoryBaseFont constructor:

#0  0x00007fff8928e4d0 in CFRetain ()
#1  0x00007fff86472906 in TInMemoryBaseFont::TInMemoryBaseFont(CGFont*) ()
#2  0x00007fff864728b8 in CTFontDescriptor::CTFontDescriptor(CGFont*, is_inmemory_t const&) ()
#3  0x00007fff8646cb30 in TDescriptorSource::CopyDescriptor(CGFont*, __CFDictionary const*) const ()
#4  0x00007fff8646c99a in TFont::InitDescriptor(CGFont*, __CTFontDescriptor const*) ()
#5  0x00007fff8646c7b4 in TFont::TFont(CGFont*, double, CGAffineTransform const*, __CTFontDescriptor const*) ()
#6  0x00007fff8646c775 in CTFontCreateWithGraphicsFont ()

It's not matched with a release upon releasing the CTFont, probably because the TInMemoryBaseFont destructor is never called (for unknown reasons).

This isn't a solution, but might help someone coming along debug the problem further.

Tor Arne
  • 721
  • 6
  • 11