views:

94

answers:

4

I am trying to centralize my app's networking code. Basically, in any of the various places that need information from a server, I create an object serverRequest of my class ServerRequest to get the information. When ServerRequest is done, it needs to send the information back to the calling object. Of course it should work asynchronously -- I don't want my app to grind to a halt while it is waiting.

This return of the information is the tricky part. It seems my options are delegation and notification. As far as I can tell, they both have their issues:

DELEGATION: I pass myself off as a delegate to the serverRequest object. The problem is that if I am deallocated before the request completes, serverRequest will be messaging a deallocated object and my program will crash. To prevent this, I would have to keep track of all my server requests (there might be more than one) and let them all know in my dealloc method so that I don't get any more messages. All of this is possible, but it sure seems like a pain.

NOTIFICATION: Seems like a lot of work to pass the information around. I have to add myself as an observer to the notification center, then remove myself when I deallocate. Furthermore, I have to pass into ServerRequest the information of what kind of notification to post when it is done. And I ServerRequest has to shove the received data into an NSDictionary, which I then get it back out of after it is passed.

Both methods ought to work, but they both seem like an awful lot of effort just to have ServerRequest wake up the calling code and pass it an object. I am thinking notification is a bit more flexible, a bit less of a pain, and a bit less likely to cause a crash, but I'm not really happy with either approach. Any feedback would be appreciated. Thanks.

A: 

you can retain the delegate passed in and you would then not be de-allocated until the server request is also finished.

e.g.

@interface ServerRequest : NSObject
{
  id delegate;
}

@property (retain) id delegate;
@end

@implementation ServerRequest
@synthesize delegate;
@end

But then you need to avoid releasing the ServerRequest from the other end, or you can make the initiator of the ServerRequest release it when it is itself released and that would take away the problem. To do that

@interface SomeObject : NSObject
{
  ServerRequest getsomedata;
}

@property (retain) ServerRequest getsomedata;
@end

- (void)f()
{
  [self setGetsomedata:[[ServerRequest alloc] init]];
  [[self getsomedata] release]; // take away the refcount from allocating, setting the property will retain
}
Erik Elmgren
A: 

You should not be retaining your delegates. see http://stackoverflow.com/questions/1459147/checking-for-a-valid-delegate-object-before-sending-it-a-message

darren
A: 

I would go with the list approach. Just have a requestController containing an NSMutableArray that keeps track of all the requests. The advantage of this is that, when your controller gets deallocated, you can do something like [requests makeObjectsPerformSelector: @selector(cancelRequest)] to stop all those requests from hogging the network. It also helps in debugging, because you can actually ask each object what requests it has pending, gauge the performance impact of many pending requests etc. When a request finishes, the request controller can be informed and can remove it from its list with a simple removeObject.

Also, someone has to own your objects. In manually managed memory, ObjC objects can retain themselves, but if you ever want to move to GC, having an array is a much cleaner solution than CFRetaining free-floating objects.

uliwitness
A: 

I encountered similar problems in the past, but chose a slightly different design (similar to what @uliwitness suggested.

I choose to separate the request (i.e. the actual content) from the delivery system. In your case that would mean the serverRequest holds the content of the request (URL, data, etc.) but doesn't do the actual communication with the server. The delegate for the server request would be a singleton CommLayer class which will actually take care of sending the request, receiving it and notifying delegates about request completion.

So to send a serverRequest you would do call something like [CommLayer sendRequest:serverRequest withDelegate:myDelegate].

Now the CommLayer is the actual class that holds the delegate not the serverRequest, and you can always notify the CommLayer that your class is not valid anymore using something like [CommLayer removeDelegate:myDelegate]

Sure it's more work, but you really get a lot of benefits from this design, to name a few:

  • Real management of network traffic. You can decide how many open connection you want at once and queue requests.
  • You can cancel unneeded requests
Ron Srebro