Cleaning up hanging NSURLConnections when a controller is dealloc'ed
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.
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;
}
当您发出请求并在获取请求完成时释放时,您需要保留您的视图控制器
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.