Question

I have a controller that makes HTTP GET requests using a custom class, which acts as the delegate for the NSURLConnection. Once the NSURLConnection fails or finishes, the custom class invokes methods on the controller and passes along an NSData object of received data.

I'm running into a problem where the controller in action is being dynamically created and pushed onto a navigation controller's stack. This controller makes the HTTP GET request in its viewDidLoad method. If the user quickly presses "back" on the navigation bar, this controller gets dealloc'ed. If this happens before the HTTP GET request finishes, the resulting NSURLConnection callback becomes a method call to a dealloc'ed object, which results in an EXC_BAD_ACCESS.

What's the best approach to cleaning up any pending NSURLConnections that have been kicked off by a controller which may actually be deallocated already?

I threw in some NSLog statements, and it seems that my custom class used as a NSURLConnection delegate doesn't actually receive a dealloc message. I made sure to set the controller's instance of this class to nil in viewDidUnload, and also call release on it, but it still seems to live longer than the controller.

Was it helpful?

Solution

If I understand it correctly you just need to do [whateverConnection cancel] in your viewDidUnload or dealloc method. This cancels the connection. It is almost the same if you have a custom downloader object for example for some large image that uses NSURLConnection. Make a cancel method for your class (that cancels the connection and releases it) and invoke it in your controller's dealloc method. You should also use a bool flag something like wasCanceled and do not invoke any method from your custom object's delegate if wasCanceled was set from your cancel method. (You only have a weak pointer to your delegate so it is probably released already when some other object invokes your cancel method). I assume that the delegate for your custom object is the view controller. I had several downloaders like this and it worked ok, without leaks even if I quickly canceled a download.

@interface CompaniesDownloader : NSObject /*<NSXMLParserDelegate>*/
{
    id<CompaniesDownloaderDelegate> delegate; //a view controller is the delegate
    NSMutableArray *companies;

    BOOL isWorking;    
    BOOL wasCanceled;   

    @private

    //url connection object
    NSURLConnection *companiesConnection;

    //this is where i put the binary data that gets transformed into xml
    NSMutableData *webData;

    //temporary string used when parsing xml
    NSMutableString *tmpString;

    //temporary company used when parsing xml
    Company *tmpCompany;
}

In the implementation:

-(void) cancel
{
  [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: FALSE];

  wasCanceled = TRUE;
  [companiesConnection cancel];
  [webData release];
  webData = nil;
  self.companiesConnection = nil; //OR [companiesConnection release]; companiesConnection=nil;
  isWorking = FALSE;
}

OTHER TIPS

You need to retain your view controller when you make a request and release when the get request finished

YourViewController.m
- (void)callGetRequest {
   [self retain];
}

- (void)didFinishAllGetTask {
   [self release];
}

If the user quickly presses "back" on the navigation bar, this controller gets dealloc'ed. If this happens before the HTTP GET request finishes, the resulting NSURLConnection callback becomes a method call to a dealloc'ed object, which results in an EXC_BAD_ACCESS.

When the user hits the back button, de-register the view controller class from being the delegate for that object. (Keep a reference to the object in the view controller class, so you can do something like someObject.delegate = nil;). You can do that in the view controller's dealloc method.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top