5

Mam projekt ARC i próbuję narysować pionowy gradient liniowy. Poniższy kod działa na symulatorze, ale powoduje błąd pamięci/EXC_BAD_ACCESS podczas testowania na urządzeniu. Aplikacja awarii na następujące dwa wiersze kodu:Problem z CFArrayRef i NSArray podczas rysowania gradientu przy użyciu ARC

NSArray *colorArray = [NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil]; 
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colorArray, colorLocations); 

Te dwa wiersze kodu są pobierane z następującego kodu (w celach informacyjnych):

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    [self createGradientForContext:context andView:self.captionView]; 
    [self createGradientForContext:context andView:self.linksView]; 
    [self createGradientForContext:context andView:self.commentView]; 

} 

- (void)createGradientForContext:(CGContextRef)context andView:(UIView *)view 
{ 

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    CGFloat colorLocations[] = { 0.0f, 1.0f }; 

    CGColorRef topColor = [[UIColor colorWithRed:51.0f/255.0f green:51.0f/255.0f blue:51.0f/255.0f alpha:1.0f] CGColor]; 
    CGColorRef bottomColor = [[UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f] CGColor]; 
    NSArray *colorArray = [NSArray arrayWithObjects:(__bridge id)topColor, (__bridge id)bottomColor, nil]; 
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge_retained CFArrayRef) colorArray, colorLocations);  

    CGRect frame = view.frame; 
    CGPoint startPoint = CGPointMake(CGRectGetMidX(frame), CGRectGetMinY(frame)); 
    CGPoint endPoint = CGPointMake(CGRectGetMidX(frame), CGRectGetMaxY(frame)); 

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

    CGGradientRelease(gradient); 
    CGColorSpaceRelease(colorSpace); 

} 

Dzięki wyprzedzeniem dla każdego i wszystkie wskazówki.

+0

Nie kochasz łuku? Zabieranie programistycznej kontroli pamięci ... Myślałem, że Apple nauczyło się ich lekcji z odśmiecaniem. Nie polecam go używać ... –

+3

Polecam używanie ARC, naprawdę musi być przekonującym powodem, aby tego nie robić. – zaph

+0

Dlaczego używasz obiektu tam, gdzie wszystko inne jest 'CFType'? Po prostu użyj 'CFArray' i nie musisz się martwić o mostkowanie. –

Odpowiedz

5

Spróbuj po prostu użyć CFArrayRef i unikaj mostkowania i zobacz, czy to ma znaczenie.

CGFloat topColorComps[] = {51.0f/255.0f, 51.0f/255.0f, 51.0f/255.0f, 1.0f}; 
CGFloat bottomColorComps[] = {48.0f/255.0f, 48.0f/255.0f, 48.0f/255.0f, 1.0f};    

CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); 
CGColorRef topColor = CGColorCreate(rgb, topColorComps); 
CGColorRef bottomColor = CGColorCreate(rgb, bottomColorComps); 
CGColorSpaceRelease(rgb); 

CFMutableArrayRef colorArray = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallbacks); 
CFArrayAppendValue(colorArray, topColor); 
CFArrayAppendValue(colorArray, bottomColor); 

CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorArray, colorLocations); 

CFRelease(colorArray); 
CFRelease(topColor); 
CFRelease(bottomColor); 
+0

Dzięki za pomoc. Niestety, zawiesza się na 'CFArrayAppendValue (colorArray, topColor)' podczas uruchamiania go na moim urządzeniu. – ArtSabintsev

+0

@ArtSabintsev: Problemem może być użycie 'UIColor'. Edytowałem powyższy przykład, tym razem jesteś właścicielem wszystkiego. – dreamlax

+0

dzięki! Teraz otrzymuję komunikat "CGColorCreateGenericRGB jest niedostępny". Patrząc w to. – ArtSabintsev

7

teraz wyjaśnić, dlaczego zostały upaść ...

Problemem jest to, te dwie linie:

CGColorRef topColor = [[UIColor colorWithRed:51.0f/255.0f green:51.0f/255.0f blue:51.0f/255.0f alpha:1.0f] CGColor]; 
CGColorRef bottomColor = [[UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f] CGColor]; 

Jest to bardzo częsty problem; jest wiele pytań na temat przepełnienia stosu przez użytkowników, którzy zostali przez to ukąszeni.

Problem polega na tym, że ponieważ te obiekty UIColor nigdy nie są przywoływane po tych liniach, ARC uwalnia je natychmiast - a ponieważ te obiekty UIColor są jedynymi właścicielami obiektów CGColor, UIColors natychmiast zwalniają CGColors, pozostawiając was dwoma martwymi Obiekty CGColor. Które następnie próbujesz umieścić w tablicy.

Jak ustalono, przełączenie z NSArray na czystą CFArray nie naprawi tego. Przełączanie z UIColor na czysty CGColor to jedno rozwiązanie; drugim jest umieszczenie własnego prawa własności na CGColors:

CGColorRef topColor = CGColorRetain([[UIColor colorWithRed:51.0f/255.0f green:51.0f/255.0f blue:51.0f/255.0f alpha:1.0f] CGColor]); 
CGColorRef bottomColor = CGColorRetain([[UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f] CGColor]); 

⋮ 

CGColorRelease(topColor); 
CGColorRelease(bottomColor); 
+3

+1. Dla mnie lepszym rozwiązaniem jest użycie UIColor aż do momentu, w którym utworzysz tablicę: [NSArray arrayWithObjects: (__ identyfikator mostu) startColour.CGColor, (__bridge id) endColour.CGColor, nil]; – Cthutu

+0

@Cthutu: To faktycznie działa tak samo, jak samo ich zatrzymanie - w takim przypadku tablica zachowuje je. Dla celów innych niż gradient, przekazywanie 'myUIColor.CGColor' bezpośrednio do' CGContextSetFillColorWithColor' lub podobnego (w przeciwieństwie do ukrywania go w zmiennej 'CGColorRef', a następnie pobierania z niej) powinno również działać, jeśli ta funkcja zachowuje obiekt koloru lub nie próbuje go zatrzymać. –

+0

Tak, to dokładnie to samo, ale nie potrzebujesz wszystkich wywołań CGColorRelease(). Wydanie odbywa się w ARC, a nie w wywołaniu Core Foundation. – Cthutu

Powiązane problemy