3

Sorry for noobish question about iPhone and Quartz programming. Just started my conversion from C++ to Objective-C :)

So, I have such a class method

+(CGGradientRef)CreateGradient:(UIColor*)startColor endColor:(UIColor*)endColor
{
    CGGradientRef result;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[2] = {0.0f, 1.0f};
    CGFloat startRed, startGreen, startBlue, startAlpha;
    CGFloat endRed, endGreen, endBlue, endAlpha;

    [endColor getRed:&endRed green:&endGreen blue:&endBlue alpha:&endAlpha];
    [startColor getRed:&startRed green:&startGreen blue:&startBlue alpha:&startAlpha];

    CGFloat componnents[8] = {
        startRed, startGreen, startBlue, startAlpha,
        endRed, endGreen, endBlue, endAlpha
    };
    result = CGGradientCreateWithColorComponents(colorSpace, componnents, locations, 2);
    CGColorSpaceRelease(colorSpace);
    return result;
}

and its usage.

-(void)FillGradientRect:(CGRect)area startColor:(UIColor *)startColor endColor:(UIColor *)endColor isVertical:(BOOL)isVertical
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    UIGraphicsPushContext(context);
    CGGradientRef gradient = [Graphics CreateGradient:startColor endColor:endColor];

    CGPoint startPoint, endPoint;
    if (isVertical) {
        startPoint = CGPointMake(CGRectGetMinX(area), area.origin.y);
        endPoint = CGPointMake(startPoint.x, area.origin.y + area.size.height);
    }else{
        startPoint = CGPointMake(0, area.size.height / 2.0f);
        endPoint = CGPointMake(area.size.width, startPoint.y);
    }

    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);

    CGGradientRelease(gradient);
    UIGraphicsPopContext();
}

everything works as expected. But, when I run the Analyze tool from Xcode 4, I'm getting a warning about memory leak in method CreateGradient for result variable. Well, I understand what's that about, but in my calling method I'm releasing the gradient object (CGGradientRelease(gradient);). So, who is wrong and how to make Analyze tool happy?

Thx

Eugen
  • 2,934
  • 2
  • 26
  • 47

2 Answers2

1

Since CGGradientRef is a Core Foundation type of object, you can autorelease it. Just add this line before returning the gradient:

[(id)result autorelease];
Costique
  • 23,712
  • 4
  • 76
  • 79
  • but as I can see it is not a descendant of NSObject? Is it ok to do so? – Eugen Nov 07 '11 at 06:42
  • Yes, unless you use ARC, because `-[NSObject release]`, `CGGradientRelease()` and `CFRelease()` basically do the same thing, they decrement the retain count and deallocate the object once the retain count reaches zero. The first two also gracefully handle `nil` objects. `-[NSObject autorelease]` just keeps the object's pointer to send `-[NSObject release]` later, typically at the end of the run loop. – Costique Nov 08 '11 at 04:51
  • really appreciate the explanation. Thank you. – Eugen Nov 08 '11 at 05:27
  • hm, there is something wrong now after I added that case. I'm getting a warning Incompatible pointer types returning 'id' from a function with result type CGGradientRef (aka struct CGGradient *) I understand I can make a cast, just not sure is ok :) – Eugen Nov 08 '11 at 07:10
0

If the goal is solely to keep the analyzer happy in ARC, then just make it a C function rather than objective-C - i.e.:

CGGradientRef CreateGradient(UIColor *startColor, UIColor * endColor)

The Core Foundation naming scheme then applies which says that a function with Create in the name is treated as returning a retained object (and it is the caller's responsibility to release it). This satisfies the analyser.

If you want an autoreleased variable, then transfer ownership of the CG type to ARC:

id arc_result = (__bridge_transfer id)result

However, if you do that, you need to return the objective-c type (arc_result), not the CG-type. If you return the CG type, there will be no retained references to arc_result, and so the compiler will clean it up as you return from the function.

You could use this hack to effect a CG-type autorelease:

dispatch_async(dispatch_get_main_queue(), ^{
     CGGradientRelease(result);
});

It would satisfy the analyser and probably work - though I would consider it to be pretty unsafe!

Airsource Ltd
  • 32,379
  • 13
  • 71
  • 75