2011-11-16 10 views
7

Tôi có mã biên dịch mà không gặp sự cố. Nó chạy tốt trên trình giả lập iPhone, nhưng trên thiết bị của tôi, tôi nhận được EXC_BAD_ACCESS.CG Gradient chạy trên trình mô phỏng nhưng không chạy trên iPhone

Điều này xảy ra trong chức năng trợ giúp để vẽ gradient. Tôi đã theo dõi this tutorial để làm điều đó. Mã tôi có là như sau:.

- (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 nổi bật dòng 12 (một với nil]; như dòng lỗi

Đối với Peter Hosey, đây là đầu ra 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 trình mô phỏng (và iPhone) chạy trên iOS 5.

Điều gì có thể gây ra sự cố này?

+0

Điều đó có thể có nghĩa là một trong startColor hoặc endColor là một con trỏ lơ lửng; bạn có thể hiển thị mã gọi drawLinearGradient không? – Tommy

+0

Tôi cập nhật gist với mã gọi – ksol

+0

Nếu bạn gõ 'po startColor' và' po endColor' vào trong Debugger Console, bạn sẽ nhận được gì? –

Trả lời

14

Một cách để giải quyết vấn đề này là chuyển UIColors vào chức năng của bạn, thay vì CGColorRefs và sử dụng một đoạn (id)[color1 CGColor] cho mỗi phần tử trong mảng colors của bạn. Đây dường như là cách phổ biến nhất mà mọi người đang giải quyết vấn đề này ngay bây giờ.

Tôi chỉ ra một lần sử dụng điều này trong this answer và có cuộc thảo luận mở rộng về điều này trong this Apple developer forum thread. Nếu bạn sử dụng phương thức -CGColor của UIColor tại thời điểm khai báo NSArray của bạn, và truyền tới id, mọi thứ sẽ được tự động bắc cầu cho bạn. Như bang gparker trong các chủ đề trên diễn đàn liên kết:

Trường hợp tự động được mô tả bởi các tài liệu chỉ áp dụng cho gọi một phương thức Objective-C trả về một kiểu CF và sau đó ngay lập tức đúc kết quả đến một Objective- Loại đối tượng C. Nếu bạn làm bất cứ điều gì khác với kết quả của phương pháp, chẳng hạn như gán cho biến số của loại CF, thì biến đó không còn tự động nữa.

Như hatfinch chỉ ra, điều này có nghĩa là CGColorRefs của bạn được đặt trong các biến tạm thời sẽ không treo xung quanh lần cuối bạn tham chiếu UIColors, trừ khi bạn giữ chúng một cách rõ ràng. Giống như những người khác trong chuỗi diễn đàn đó, tôi đã nhầm tưởng rằng đây là một lỗi trong việc thực hiện chuyển tiếp, nhưng tôi có thể thấy rằng tôi đã đọc sai điều này.

+2

Đó thực sự là một lỗi ARC, mặc dù? [Thông số ARC] (http://clang.llvm.org/docs/AutomaticReferenceCounting.html) nói rằng tham chiếu đối tượng CF không được coi là “con trỏ đối tượng có thể lưu lại”, ngụ ý rằng ARC không có nghĩa là giữ lại các CGColors. Các UIColors không được tham chiếu sau khi các CGColors được trích xuất từ ​​chúng, vì vậy không có lý do đáng tin cậy để cho rằng chúng tồn tại lâu hơn thế. Nếu UIColors sở hữu CGColors, họ sẽ lấy CGColors cùng với họ khi họ bị deallocated. –

+1

Cách giải quyết của bạn sẽ hoạt động, nhưng không phải vì ARC có lỗi; nó hoạt động như dự định (xem bình luận của Peter và câu trả lời của tôi). – hatfinch

+0

@PeterHosey - Ah, không hoàn toàn là lỗi, vì tôi đã bỏ lỡ điều này gần đầu chuỗi diễn đàn được liên kết: "Trường hợp tự động được mô tả bằng tài liệu chỉ áp dụng để gọi phương thức Objective-C trả về loại CF và sau đó truyền ngay kết quả cho một loại đối tượng Objective C. Nếu bạn làm bất cứ điều gì khác với kết quả phương pháp, chẳng hạn như gán nó cho một biến kiểu CF, thì nó không còn tự động nữa. " Nó có vẻ giống như một lỗi, bởi vì việc sử dụng trực tiếp trong NSArray làm việc, nhưng CGColorRef tạm thời chuyển nhượng không. Phương pháp '-CGColor' có vẻ là một trường hợp đặc biệt. –

12

Bạn không giữ màu trắngColor và lightGrayColor còn sống. Bạn có được CGColorRefs bạn không sở hữu từ UIColors mà không bao giờ được giữ lại. code của bạn nên đọc:

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

Bạn có thể kiến ​​nghị của Apple tuyên bố - [UIColor CGColor] như objc_returns_inner_pointer, mà sẽ làm cho mã của bạn đơn giản hơn, nhưng thuộc tính đó là thực sự dành cho con trỏ không retainable.

+0

Điều đó có ý nghĩa hơn nhiều so với giả định sai lầm mà tôi (và những người khác) đã biết rằng đây là lỗi ARC. Có vẻ như việc truyền trực tiếp tới NSObject khi thoát khỏi phương pháp '-CGColor' là một trường hợp đặc biệt và điều đó gây ra cho tôi một số rắc rối ở đây. –