2010-04-21 3 views
17

Có bộ điều khiển chế độ xem và nó tạo đối tượng "trình tải xuống", có tham chiếu đến bộ điều khiển chế độ xem (dưới dạng đại biểu). Trình tải xuống sẽ gọi lại trình điều khiển chế độ xem nếu nó tải xuống thành công mục đó. Điều này làm việc tốt miễn là bạn ở lại trên xem, nhưng nếu bạn điều hướng đi trước khi tải về hoàn tất tôi nhận được EXC_BAD_ACCESS. Tôi hiểu tại sao điều này xảy ra, nhưng có cách nào để kiểm tra xem một đối tượng vẫn được phân bổ không? Tôi đã cố gắng kiểm tra bằng cách sử dụng delegate != nil[delegate respondsToSelector:], nhưng nó sẽ bị nghẹt thở.Tránh EXC_BAD_ACCESS khi sử dụng mẫu đại biểu

if (!self.delegate || ![self.delegate respondsToSelector:@selector(downloadComplete:)]) { 
    // delegate is gone, go away quietly 
     [self autorelease]; 
     return; 
    } 
else { 
    // delegate is still around 
    [self.delegate downloadComplete:result]; 
} 

Tôi biết tôi có thể,

a) có các đối tượng downloader giữ lại bộ điều khiển xem

b) giữ một mảng các phần mềm download trong bộ điều khiển xem, và thiết lập các giá trị đại diện của họ để nil khi Tôi deallocate bộ điều khiển xem.

Nhưng tôi tự hỏi nếu có cách nào dễ dàng hơn, tôi chỉ kiểm tra xem địa chỉ đại biểu có chứa đối tượng hợp lệ không?

+0

Nếu bạn có thể kiểm tra xem một địa chỉ có chứa một đối tượng hợp lệ, theo định nghĩa, nó sẽ, bởi vì nó sẽ là hợp lệ để truy cập nó để kiểm tra? –

+1

Object-c có rất nhiều trừu tượng ... Tôi có thể tưởng tượng một thế giới mà thời gian chạy có thể cho biết sự khác biệt giữa một địa chỉ với một đối tượng hợp lệ, và một deallocated một. –

Trả lời

10

Không, bạn không thể (hữu ích) "kiểm tra xem địa chỉ có chứa đối tượng hợp lệ không". Ngay cả khi bạn có thể grub xung quanh bên trong nội bộ của hệ thống cấp phát bộ nhớ và xác định rằng địa chỉ của bạn trỏ đến một đối tượng hợp lệ, điều đó không nhất thiết có nghĩa là đối tượng cùng một đối tượng mà bạn đã đề cập trước đó: đối tượng có thể đã được deallocated và một đối tượng được tạo ra tại cùng một địa chỉ bộ nhớ.

Giữ lại đại biểu là cách thông thường để giải quyết vấn đề này. Tùy chọn của bạn (b) ngắt đóng gói đối tượng và có thể có vấn đề về an toàn luồng.

+0

hầu hết các triển khai của apple tôi có thể thấy không giữ lại người được ủy quyền ... ví dụ: '@property (nonatomic, assign) id delegate;' Tôi có áp dụng sai mẫu thiết kế không? –

+0

Điều đó có thể đúng đối với các đối tượng giao diện người dùng yêu cầu tất cả tương tác xảy ra trên luồng chính, nhưng tôi nghĩ bạn sẽ thấy rằng việc triển khai thực hiện thao tác không đồng bộ trong chủ đề nền (chẳng hạn NSURLConnection) sẽ giữ lại ủy quyền của họ. –

+6

** Đại biểu ** KHÔNG BAO GIỜ được giữ lại! Chỉ ** mục tiêu ** được giữ lại. –

1

Tôi chỉ sẽ viết

SEL slc = @selector(theSlc); 
if ([delegate respondsToSelector:slc]) { 
    [delegate performSelector:slc]; 
} 

Nếu đối tượng có giá trị phương pháp này sẽ được gọi, và ngược lại. Bạn không phải kiểm tra

self.delegate != nil 
+4

Bạn có lẽ đúng là 'self.delegate' của tôi là thừa. Nhưng, tôi biết rằng '[delegate respondsToSelector:]' không bảo vệ bạn khỏi 'EXC_BAD_ACCESS'. Hãy thử chạy ứng dụng này: \t NSObject * obj = [[NSObject alloc] init]; \t [phát hành obj]; \t cho (int i = 0; i <2; i ++) { \t \t nếu ([obj respondsToSelector: @selector (retainCount)]) NSLog (@ "giữ lại số:% i", [obj retainCount]); \t} –

1

Tôi gặp phải câu hỏi này vì đối tượng "trình tải xuống" của tôi đã cho tôi EXC_BAD_ACCESS. Giải pháp của tôi là hủy bỏ đối tượng downloader ngay trước khi tôi phát hành nó. Giả sử bạn sử dụng NSURLConnection trong đối tượng downloader của bạn, hãy gọi phương thức hủy bỏ nó.

Điều quan trọng cần lưu ý là nếu NSURLConnection hiện không tải xuống bất kỳ thứ gì thì việc hủy cuộc gọi sẽ dẫn đến sự cố. Bạn sẽ cần một số logic để kiểm tra xem có đang tải xuống hay không.

1

Tôi cũng có vấn đề với tham chiếu yếu của đại biểu và hiện tại tôi chỉ có một giải pháp cho vấn đề này: sử dụng tham chiếu mạnh cho đại biểu và đặt self.delegate = nil theo cách thủ công; sau khi mã của tôi hoàn tất. Giải pháp này làm việc cho tôi trong tải hình ảnh không đồng bộ, nơi bạn có một số vòng đời với kết thúc có thể nhìn thấy.

27

Tôi vừa gặp sự cố này và đã giải quyết vấn đề. Đối với ARC, giải pháp là sử dụng thuộc tính weak thay vì assign.

Vụ tai nạn xảy ra bởi vì các đại biểu

  1. Có một thuộc tính assign
  2. Đã được deallocated.

Giải pháp là sử dụng thuộc tính weak, bởi vì khi deallocates đối tượng, con trỏ WILL được thiết lập nil. Vì vậy, khi mã của bạn gọi respondsToSelector trên nil, Mục tiêu C sẽ bỏ qua cuộc gọi và không bị lỗi.

Trong mã của bạn, khi bạn cố gọi phương thức respondsToSelector trên delegate, bạn sẽ nhận được EXC_BAD_ACCESS. Điều này là do các đối tượng sử dụng thuộc tính assign sẽ không được đặt thành nil khi chúng được deallocated. (Do đó tại sao làm một !self.delegate trước respondsToSelector không ngăn cản responseToSelector tránh bị gọi trên một đối tượng deallocated, và vẫn còn bị treo mã của bạn)

Như đã đề cập, sử dụng một thuộc tính strong hoặc assign trên một đại biểu (như nhiều người đã đề cập) trong ARC sẽ dẫn đến một chu trình giữ lại. Vì vậy, không làm điều đó, bạn không cần phải.

+0

ngoài việc sử dụng yếu, có những thứ khác cần thiết không? Ví dụ: 'delegate = nil' trong lớp chính hoặc đặt 'id _strong delegate'? Tôi giả sử im haivng cùng một vấn đề http://stackoverflow.com/questions/24466244/delegate-dont-exist-exc-bad-access/24970685#24970685 – Hexark

+2

Bạn không cần phải làm gì khác. Tôi đọc câu hỏi của bạn và nhận thấy rằng bạn đã cung cấp câu trả lời cho biết rằng việc sử dụng 'weak' thay vì' assign' đã sửa lỗi. Bạn vẫn gặp sự cố? –

+0

Dường như vấn đề đã được giải quyết :) – Hexark