Memory leak with CGGradientRef

Discussion in 'Mac Programming' started by North Bronson, May 10, 2009.

  1. North Bronson macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #1
    I've had to do some drawing lately. I've been working with Core Graphics and think I know my way around.

    I keep getting this memory leak that's driving me nuts. I have no idea what's happening. It's happening whenever I draw a gradient. Has anyone else seen this?

    I didn't have too much experience managing memory with Core Foundation -- I've made some pretty air-tight Cocoa apps, though. I've tried everything I can think of and this leak is still here. Any ideas?

    I create a CustomView that is also the Application Delegate. Here is the entire code that I am running:

    Code:
    @interface CustomView: NSView
    
    @end
    
    @implementation CustomView
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    {
        NSWindow *newWindow = [[NSWindow alloc] initWithContentRect: NSMakeRect(0, 0, 256, 512) styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask backing: NSBackingStoreBuffered defer: NO];
        
        CustomView *newCustomView = [[CustomView alloc] initWithFrame: [[newWindow contentView] bounds]];
        
        [newWindow setContentView: newCustomView];
        
        [newCustomView release];
        
        [newWindow makeKeyAndOrderFront: nil];
    }
    
    
    
    - (void)drawRect:(NSRect)rect
    {
        CGContextRef currentContext = [[NSGraphicsContext currentContext] graphicsPort];
        
        CGColorSpaceRef newColorSpace = CGColorSpaceCreateDeviceGray();
        
        CGFloat components[4] = { 1 , 1 , 0 , 1 };
        
        CGGradientRef newGradient = CGGradientCreateWithColorComponents(newColorSpace, components, NULL, 2);
        
        CGColorSpaceRelease(newColorSpace);
        
    *** CGContextDrawLinearGradient(currentContext, newGradient, CGPointZero, CGPointMake(0, rect.size.height), 0);
        
        CGGradientRelease(newGradient);
    }
    
    @end
    When I comment out the *** line, the memory leak goes away.

    Any ideas?
     
  2. kpua macrumors 6502

    Joined:
    Jul 25, 2006
    #2
    Your code does seem to obey CF's 'create rule' just fine.

    I assume you're finding the leak with 'leaks'? The leaks tool is known to occasionally make false positives, especially if code does funny stuff with pointers.

    Have you tried calling that gradient draw function in a loop and seeing if the memory usage of your app increases unbounded?

    Either way, I'd file a bug, either about the leak, or the false positive.
     
  3. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #3
    You could also try NSGradient and see if the leak shows there as well.
     
  4. North Bronson thread starter macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #4
    I tried your idea with the loop. The memory just kept going up and up. I commented out the drawing and the memory stayed flat.

    Well, I was kind of hoping to use some of this drawing for my iPhone app; it looks like I can't use NSGradient over there -- that's the reason I was going down to Core Graphics in the first place.
     
  5. kpua macrumors 6502

    Joined:
    Jul 25, 2006
    #5
    Unbounded memory growth usually attests to a genuine leak.

    One more thing to check, just for kicks and giggles though. Bracket the code with an autorelease pool -new and -flush. I highly doubt CGGradient's uses autoreleased memory, but it's technically possible.

    Also, instead of a loop, since I noticed you're drawing in drawRect:, try invalidating the view (causing redraw) with a timer every second or faster. That will give the graphics context to flush everything out in between draws. If memory usage still grows unbounded, I think you can be pretty confident you've got a genuine bug in Apple's code there.

    What release are you on, BTW? Also, GC or non-GC app? (You mentioned the iPhone, so I'm guessing non-GC)
     
  6. North Bronson thread starter macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #6
    I'm running the latest version of Leopard. I have 10.5.6 Build 9G55.

    I'm running Xcode 3.1.2. I haven't started using the iPhone 3.0 Beta, yet.

    I haven't been using garbage collection.
     
  7. North Bronson thread starter macrumors 6502

    Joined:
    Oct 31, 2007
    Location:
    San José
    #7
    I think I found out how to fix this.

    Code:
    - (void)drawRect:(NSRect)rect
    {
        CGContextRef currentContext = [[NSGraphicsContext currentContext] graphicsPort];
        
        CGColorSpaceRef newColorSpace = CGColorSpaceCreateDeviceGray();
        
        CGFloat gradientComponents[4] = { 1 , 1 , 0 , 1 };
        
        
        
        CGGradientRef newGradient = CGGradientCreateWithColorComponents(newColorSpace, gradientComponents, NULL, 2);
        
        CGColorSpaceRelease(newColorSpace);
        
        
        
    //  CGFunctionRef newGradientFunction = CGGradientGetFunction(newGradient);
        
        CGContextDrawLinearGradient(currentContext, newGradient, CGPointZero, CGPointMake(0, rect.size.height), 0);
        
        CGGradientRelease(newGradient);
        
    //  CGFunctionRelease(newGradientFunction);
    }
    If you run it this way, it will leak. If you uncomment out those two lines, it will not leak -- at least that's what it looks like over here.
     
  8. kpua macrumors 6502

    Joined:
    Jul 25, 2006
    #8
    Is that function private API? I don't see it in CGGradient's documentation.

    Also, that code violates the "Create rule", since you're releasing a "get" prefixed function's return value.

    It may very well be that this CGFunctionRef is what is being leaked internally, but when the leak is fixed, your app will crash. I don't recommend doing this in publicly released software, unless you're willing to deal with updating your application to unfix your fix.
     

Share This Page