views:

162

answers:

2

A have a view controller, and it creates a "downloader" object, which has a reference to the view controller (as a delegate). The downloader calls back the view controller if it successfully downloads the item. This works fine as long as you stay on the view, but if you navigate away before the download is complete I get EXC_BAD_ACCESS. I understand why this is happening, but is there any way to check if an object is still allocated? I tried to test using delegate != nil, and [delegate respondsToSelector:], but it chokes.

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];
}

I know I could,

a) have the downloader objects retain the view controller

b) keep an array of downloaders in the view controller, and set their delegate values to nil when I deallocate the view controller.

But I wonder if there is an easier way, where I just test if the delegate address contains a valid object?

+1  A: 

No, you can't (usefully) "test if an address contains a valid object". Even if you were able to grub around inside the internals of the memory allocation system and determine that your address points to a valid object, that would not necessarily mean that it was the same object that you were previously referring to: the object could have been deallocated and another object created at the same memory address.

Retaining the delegate is the usual way to solve this. Your option (b) breaks object encapsulation, and might have thread-safety issues.

David Gelhar
most of apple's implementations I can see don't retain the delegate...e.g. `@property(nonatomic,assign) id <UITableViewDelegate> delegate;` am I mis-applying the design pattern?
Kenny Winker
That may be true for UI objects that require all interaction to happen on the main thread, but I think you'll find that implementations that are doing async operations in background threads (such as NSURLConnection) will retain their delegate.
David Gelhar
A **delegate** is NEVER retained! Only **targets** are retained.
Erik Aigner
Could you please explain more or provide a reference, @eaigner. I googled a bit and can find no definition of a __target__.
Kenny Winker
A delegate is an object that receives events, but is not retained by the sender. A target - for instance a NSButton/Control has a target and an action - is retained by the sender.
Erik Aigner
@eaigner What's your reference for the claim that a delegate is never retained? For example, the NSURLConnection method `-initWithRequest:delegate:` *does* retain the delegate (and is documented to do so).
David Gelhar
A: 

I would just write

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

If the object is valid the method will be called, otherwise not. You do not have to check for

self.delegate != nil
Erik Aigner
You're probably right that my `!self.delegate` is redundant. But, I do know that `[delegate respondsToSelector:]` doesn't protect you from `EXC_BAD_ACCESS`. Try running this: NSObject *obj = [[NSObject alloc] init]; [obj release]; for (int i = 0; i<2; i++) { if ([obj respondsToSelector:@selector(retainCount)]) NSLog(@"retain count: %i",[obj retainCount]); }
Kenny Winker