CG Gradient работает на симуляторе, но не на iPhone - PullRequest
7 голосов
/ 16 ноября 2011

У меня есть код, который компилируется без проблем. Он хорошо работает на симуляторе iPhone, но на моем устройстве я получаю EXC_BAD_ACCESS.

Это происходит во вспомогательной функции для рисования градиента. Я следовал этому уроку , чтобы сделать это. У меня есть следующий код:

- (void) drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGColorRef whiteColor = [UIColor whiteColor].CGColor;
    CGColorRef lightGrayColor = [UIColor colorWithRed:230.0/255.0
                                                green:230.0/255.0 
                                                 blue:230.0/255.0 
                                                alpha:1.0].CGColor;
    CGColorRef separatorColor = [UIColor colorWithRed:208.0/255.0
                                                green:208.0/255.0 
                                                 blue:208.0/255.0
                                                alpha:1.0].CGColor;
    CGRect paperRect = self.bounds;
    CGRect nameRect = self.nameLabel.frame;
    CGPoint sepStartPoint = CGPointMake(nameRect.origin.x, 
                                        nameRect.origin.x + nameRect.size.height + 2);
    CGPoint sepEndPoint = CGPointMake(nameRect.origin.x + nameRect.size.width, 
                                      nameRect.origin.x + nameRect.size.height + 2);

    drawLinearGradient(context, paperRect, lightGrayColor, whiteColor);
    draw1PxStroke(context, sepStartPoint, sepEndPoint, separatorColor);

}


// Callee, where the problem is
void drawLinearGradient(CGContextRef context,
                        CGRect rect,
                        CGColorRef startColor, 
                        CGColorRef endColor)
{
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[] = { 0.0, 1.0 };

    NSArray *colors = [NSArray arrayWithObjects:
                       (__bridge id)startColor,
                       (__bridge id)endColor,
                       nil]; // Here is the line

    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, 
                                                        (__bridge CFArrayRef) colors, locations);

    CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));

    CGContextSaveGState(context);
    CGContextAddRect(context, rect);
    CGContextClip(context);
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    CGContextRestoreGState(context);

    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}

Xcode выделяет строку 12 (ту, где nil]; в качестве строки ошибки.

Для Питера Хоси вот вывод отладчика:

(gdb) po startColor
<CGColor 0x1deca0> [<CGColorSpace 0x1d3280> (kCGColorSpaceDeviceGray)] ( 1 1 )
Current language:  auto; currently objective-c
(gdb) po endColor
<CGColorSpace 0x1bf120> (kCGColorSpaceDeviceRGB)
(gbd)

Мой симулятор (и iPhone) работает на iOS 5.

Что может быть причиной этого сбоя?

Ответы [ 2 ]

14 голосов
/ 17 ноября 2011

Один из способов обойти это - передать в вашу функцию UIColors вместо CGColorRefs и использовать (id)[color1 CGColor] приведение для каждого элемента вашего массива colors.Похоже, это самый популярный способ решения этой проблемы людьми прямо сейчас.

Я отмечаю одно использование этого в этом ответе , а в есть расширенное обсуждение этого вопроса.это ветка форума разработчиков Apple .Если вы используете метод UIColor -CGColor в момент объявления NSArray и приведения к id, все автоматически соединится для вас.Как утверждает gparker в ветке форума, указанной выше:

Автоматический случай, описанный в документации, применяется только к вызову метода Objective-C, который возвращает тип CF, а затем немедленно приводит результат к Objective-C тип объекта.Если вы сделаете что-нибудь еще с результатом метода, например присвоите его переменной типа CF, то он больше не будет автоматическим.

Как указывает хетфинч, это может означать, что ваши CGColorRefs помещеныво временных переменных не будет зависать после последней ссылки на ваш UIColors, если вы явно не сохраните их.Как и другие участники этой ветки форума, я ошибочно думал, что это ошибка в реализации мостового соединения, но я вижу, что я неправильно это читал.

12 голосов
/ 17 ноября 2011

Вы не поддерживаете свой whiteColor и lightGrayColor. Вы получаете CGColorRefs, которыми вы не владеете, от UIColors, которые никогда не сохраняются. Ваш код должен читать:

CGColorRef whiteColor = CFRetain([UIColor whiteColor].CGColor);
CGColorRef lightGrayColor = CFRetain([UIColor colorWithRed:230.0/255.0 green:230.0/255.0 blue:230.0/255.0 alpha:1.0].CGColor);
CGColorRef separatorColor = CFRetain([UIColor colorWithRed:208.0/255.0 green:208.0/255.0 blue:208.0/255.0 alpha:1.0].CGColor);

// ...

drawLinearGradient(context, paperRect, lightGrayColor, whiteColor);
draw1PxStroke(context, sepStartPoint, sepEndPoint, separatorColor);

CFRelease(whiteColor);
CFRelease(lightGrayColor);
CFRelease(separatorColor);

Вы можете попросить Apple объявить - [UIColor CGColor] как objc_returns_inner_pointer, что упростит ваш код, но этот атрибут действительно зарезервирован для не сохраняемых указателей.

...