Создание градиента и возврат для метода

#ios #ios4 #quartz-graphics

#iOS #ios4 #quartz-графика

Вопрос:

Извините за глупый вопрос о программировании iPhone и Quartz. Только что начал мое преобразование с C на Objective-C 🙂

Итак, у меня есть такой метод класса

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

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

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

и его использование.

 -(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();
}
  

все работает так, как ожидалось. Но, когда я запускаю инструмент анализа из Xcode 4, я получаю предупреждение об утечке памяти в методе CreateGradient для result переменной. Ну, я понимаю, о чем это, но в моем вызывающем методе я освобождаю объект gradient ( CGGradientRelease(gradient); ).
Итак, кто же не прав и как сделать инструмент анализа счастливым?

Спасибо

Ответ №1:

Поскольку CGGradientRef является типом объекта Core Foundation, вы можете автоматически его выпустить. Просто добавьте эту строку перед возвратом градиента:

 [(id)result autorelease];
  

Комментарии:

1. но, как я вижу, это не потомок NSObject? Нормально ли это делать?

2. Да, если вы не используете ARC, потому что -[NSObject release] , CGGradientRelease() и CFRelease() в основном делают то же самое, они уменьшают количество сохранений и освобождают объект, как только количество сохранений достигает нуля. Первые два также изящно обрабатывают nil объекты. -[NSObject autorelease] просто сохраняет указатель объекта для отправки -[NSObject release] позже, обычно в конце цикла выполнения.

3. действительно ценю объяснение. Спасибо.

4. хм, теперь что-то не так после того, как я добавил этот случай. Я получаю предупреждение о несовместимых типах указателей, возвращающих ‘id’ из функции с типом результата CGGradientRef (он же struct CGGradient *) Я понимаю, что могу создать приведение, просто не уверен, что все в порядке 🙂

Ответ №2:

Если цель состоит исключительно в том, чтобы анализатор был доволен в ARC, тогда просто сделайте его функцией C, а не objective-C — т.е.:

 CGGradientRef CreateGradient(UIColor *startColor, UIColor * endColor)
  

Затем применяется схема именования Core Foundation, в которой говорится, что функция с Create в имени рассматривается как возвращающая сохраненный объект (и ответственность за его освобождение лежит на вызывающем). Это удовлетворяет анализатор.

Если вам нужна автоматически освобождаемая переменная, передайте право собственности на тип CG в ARC:

 id arc_result = (__bridge_transfer id)result
  

Однако, если вы это сделаете, вам нужно вернуть тип objective-c (arc_result), а не CG-type. Если вы вернете тип CG, не будет сохраненных ссылок на arc_result, и поэтому компилятор очистит его при возврате из функции.

Вы могли бы использовать этот взлом для автоматического выпуска CG-типа:

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

Это удовлетворило бы анализатор и, вероятно, сработало бы, хотя я бы счел это довольно небезопасным!