views:

485

answers:

4

I am trying to implement the delegate Pattern in Objective-C, however I am experiencing a Bad Access exception when invoking the delegate sometimes. It seems this is caused by the delegate being released. Apple does not recommend to retain delegates.

How can I check my delegate if is still valid before trying to send it a message?

+7  A: 

If there's a chance that the delegate will get released by the setter, then there's something wrong with your design. You should only set delegates on objects that have a shorter lifespan than the delegate itself. For example, setting a delegate on a subview/controller is fine, because the subview/controller has a shorter lifespan than the caller.

AFAIK, there is no reliable way to detect if an object has been released already.

Philippe Leybaert
To see if an object has been released completely - send a log message in either the `dealloc` or `finalize` methods of the class.
Abizern
@Abizem: that won't help in this case. The question is whether you can detect if an object has been released (deallocated). I don't think that's possible at all without cooperation of the object itself.
Philippe Leybaert
I meant that the class sends the log messages when it's deallocated completely.
Abizern
Abizern: That would be “cooperation of the [deallocated] object itself”.
Peter Hosey
@Peter: exactly.
Philippe Leybaert
It's fine to set a delegate that could have a shorter lifespan, you just have to make sure that the object delegated to sets the delegate to nil in its dealloc method.
smorgan
+3  A: 

What Apple means about not retaining delegates is that objects should not retain their delegates because they don't own them. These are only objects that handle messages.

That doesn't mean that you shouldn't retain delegates at all. The object that creates the delegate needs to own it. In the context of non-GC apps this means it should handle the retain and release cycle, and for GC apps, it means that the controller object keeps hold of a pointer to the delegate in an iVar.

without seeing some code or the error message, it is hard to find the root of this problem.

Abizern
+1  A: 

You shouldn't need to check if the delegate is set because calls to nil will pass through in objective-C.

However, to check if the delegate implements a method use the following which is documented under NSObject.

if( [delegate respondsToSelector:@selector(aMethod)] ) {
  [delegate aMethod];
}

If you are writing code where the delegate should implement all delegate methods then define a delegate protocol. Then you won't need an if block around each callout.

Just like with an if block to check if a delegate implements a method you might want to add try catch blocks in case the delegate has been released.

Matthieu Cormier
That's not really what this question is about.
Philippe Leybaert
A: 

In a photoviewer application I'm using asynchronous http to load images; it happens that the user often dismisses the current view (referenced by my async http object through a delegate) before the http download completed causing a BAD_ACCESS when calling the view controller delegate method. I solved this by setting the .delegate to nil inside the dealloc block of the view controller

Nico Tranquilli
You may want to cancel and release the asynchronous HTTP download object instead, especially if your app is for the iPhone. The App Store reviewers don't take kindly to excessive bandwidth usage, such as when your app continues downloading an image after the user has already backed out of it. Ideally, save the partial file in the temporary-files directory, and resume the download if the user comes back to the image.
Peter Hosey