Cocoa – How should I return an NSError object from a delegate method

apicocoa

I'm designing a delegate method that is called when the remote server needs input from the delegate. The delegate at that point is responsible for filling in data to be sent to the server and also telling the server if there is an error at that point.

The delegate method I have right now is:

-(void)server:(MBServer*)server
 willSendData:(NSMutableString*)data
        error:(NSError**)error;

At this point, the delegate can provide data for the server to send by modifying the NSMutableString that is passed in. Additionally, it can create an NSError to be sent back by setting the error variable to the new NSError instance.

The problem I have with this is that the delegate method might be called from another thread via an NSInvocation. The delegate can create a new NSError object, but it will be autoreleased in the current thread before the other thread receives the response.

In a single-threaded app, this wouldn't be a problem because the delegator could just retain the response straight away but I can't do that in a multi-threaded design (again because the current event loop will autorelease it). The delegate MUST retain the error so the delegator can release it.

This is for a public api and I don't like the idea of requiring via documentation that implementers know they have to retain the error object. It seems too unsafe.

// You *must* retain the returned error before
// passing it back or you will crash

-(void)server:(MBServer*)server
 willSendData:(NSMutableString*)data
        error:(NSError**)error;

Other ideas I have thought of were to send an NSError as the return value (seems somewhat non-standard as most methods return a bool and take an NSError double-pointer). It also just looks weird.

-(NSError *)server:(MBServer*)server
      willSendData:(NSMutableString*)data;

I've also thought of passing a mutable dictionary and asking the delegate to fill in an error. The dictionary would then retain the error, safely getting it back across threads.

// Send your NSError response in the dictionary with the key
// MBServerErrorResponseKey

-(void)server:(MBServer*)server
 willSendData:(NSMutableString*)data
     response:(NSMutableDictionary*)response;

Has anyone dealt with this design problem and if so what was your solution?

Thank you.

Best Answer

How about this: instead of calling the delegate method directly, call a "trampoline" method via NSInvocation. The trampoline (which you would implement) would just call the delegate method, and retain the error before returning. Putting it another way: the trampoline (which would probably be a method on your server object) would just wrap the call to the delegate with the passed arguments, then retain the error and return.

Related Topic