2011-11-16 15 views
7

Mam kod, który kompiluje się bez problemów. Działa dobrze na symulatorze iPhone'a, ale na moim urządzeniu otrzymuję EXC_BAD_ACCESS.Gradient CG działa na symulatorze, ale nie na iPhonie

Dzieje się to w funkcji pomocnika do narysowania gradientu. Śledziłem this tutorial, aby to zrobić. Kod mam to w następujący sposób:.

- (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 podkreśla linia 12 (jeden z nil]; jako linii błędu

Peter Hosey, oto wyjście debugger:

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

My symulator (i iPhone) działa na iOS 5.

Co może być przyczyną awarii?

+0

To prawdopodobnie oznacza, że ​​jeden z elementów startColor lub endColor jest zwisającym wskaźnikiem; czy możesz wyświetlić kod wywołujący drawLinearGradient? – Tommy

+0

Poprawiłem istotę kodu wywołującego – ksol

+0

Jeśli wpiszesz 'po startColor' i' po endColor' w konsoli debuggera, co otrzymasz? –

Odpowiedz

14

Jednym ze sposobów obejścia tego problemu byłoby przekazanie funkcji UIColors zamiast funkcji CGColorRefs i użycie rzutowania (id)[color1 CGColor] dla każdego elementu z tablicy colors. Wydaje się to być najbardziej popularnym sposobem, w jaki ludzie rozwiązują teraz ten problem.

Wskazuję jedno użycie tego w this answer, a dyskusja na ten temat jest szersza w this Apple developer forum thread. Jeśli użyjesz metody UIColor -CGColor w momencie deklarowania swojej NSArray i przesyłania do id, wszystko automatycznie zostanie dla ciebie zmostkowane. Jak stwierdza gparker w wyżej połączone wątku:

Automatyczny przypadek opisany w dokumentacji dotyczy tylko wywołanie metody Objective-C, która zwraca typ CF, a następnie natychmiast odlewania wynik do Objective- Typ obiektu C. Jeśli wykonasz jakikolwiek inny wynik metody, taki jak przypisanie zmiennej typu CF, to nie jest już ona automatyczna.

Jak wskazuje hatalche, może to oznaczać, że twoje CGColorRefs umieszczone w zmiennych tymczasowych nie będą się kręcić po ostatnim odwołaniu się do twoich UIColors, chyba że je jawnie zatrzymasz. Podobnie jak inni w wątku na forum, błędnie pomyślałem, że to błąd w implementacji pomostowej, ale widzę, że źle to odczytałem.

+2

Czy to jednak błąd ARC? [Specyfikacja ARC] (http://clang.llvm.org/docs/AutomaticReferenceCounting.html) mówi, że referencje obiektów CF nie są uważane za "wskaźniki obiektów do zatrzymania", co oznacza, że ​​ARC nie ma zachować CGColors. UIColors nie są przywoływane po wyodrębnieniu z nich CGColors, więc nie można przewidzieć, że będą żyli dłużej. Jeśli UIColors są właścicielami CGColors, wezmą CGColors z nimi, gdy zostaną zwolnieni. –

+1

Twoje obejście zadziała, ale nie dlatego, że ARC ma błąd; zachowuje się zgodnie z przeznaczeniem (patrz komentarz Piotra i moja odpowiedź). – hatfinch

+0

@PeterHosey - Ah, niezupełnie błąd, ponieważ brakowało mi tego w górnej części przyłączonego wątku na forum: "Automatyczny przypadek opisany w dokumentacji dotyczy tylko wywołania metody Objective-C, która zwraca typ CF, a następnie natychmiast rzutuje wynik dla obiektu typu Objective C. Jeśli zrobisz cokolwiek innego z wynikiem metody, takim jak przypisanie go do zmiennej typu CF, to nie jest już automatyczne. " Wydawało się, że jest to błąd z powodu bezpośredniego użycia w działaniu NSArray, ale tymczasowe przypisanie CGColorRef nie. Metoda '-CGColor' wydaje się być szczególnym przypadkiem. –

12

Nie utrzymujesz przy życiu swojego whiteColor i lightGrayColor. Otrzymujesz CGColorRefs, których nie posiadasz od UIColors, które nigdy nie są przechowywane. Kod powinien brzmieć:

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

Można petycji Apple zadeklarować - [UIColor CGColor] jako objc_returns_inner_pointer, które mogłyby uczynić Twój kod prostsze, ale że atrybut jest naprawdę zarezerwowane dla non-Wygodne do przechowywania wskaźników.

+0

To ma dużo więcej sensu niż błędne założenie, że ja (i inni) stwierdziłem, że jest to błąd ARC. Brzmi to tak, jakby bezpośrednie rzutowanie na NSObject przy wyjściu z metody '-CGColor' było specjalnym przypadkiem, co spowodowało tutaj pewne zamieszanie. –

Powiązane problemy