#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);
});
Это удовлетворило бы анализатор и, вероятно, сработало бы, хотя я бы счел это довольно небезопасным!