NSURLConnection inherent memory leak? - iphone

In the example in the Xcode Documentation : URL Loading System Programming Guide : Using NSURLConnection, they alloc an NSURLConnection (theConnection) and then release it in the callbacks: -connection:didFailWithError: & -connectionDidFinishLoading:. But, won't theConnection leak (i.e. never get released) if the delegate is released before either of the callback methods get called?

No, theConnection won't leak because, as the Xcode Documentation for -[NSURLConnection initWithRequest:delegate:] states under Special Considerations: "The connection retains delegate. It releases delegate when the connection finishes loading, fails, or is canceled."

Related

Cocoa Touch begin command AFTER viewDidLoad

I have an application in which I am required to connect to the internet after a view is loaded. However, if I put this code in the viewDidLoad method the parent view freezes, and then unfreezes after the connection onto the new view. However, I would like the new view to load FIRST, and then to start the connection. I tried using viewDidAppear:, however I am getting the same issue.
Also, will any animations continue playing during the connection? Will the UI be responsive? If not, is multithreading the way to go?
Here is some of my code:
-(void)viewDidLoad {
[super viewDidLoad];
//Do some other view initialization
//Connect is a class I use to connect to the internet
[Connect getData:someString];
}
When I put the code in viewDidAppear the same thing happens.
Connection code:
NSMutableURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSHTTPURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
Also, I forgot to mention that I am running a regular expression as well after the connection.
As the name of the method says, the view has already been loaded when viewDidLoad executes.
Generally, be sure to use asynchronous connections to connect to the internet. Never block the main thread.
it is easier than you may think.
All you need is some thread management. On the view did Do:
[NSThread detachNewThreadSelector:#selector(yourMethod:) toTarget:yourTarget withObject:yourObject];
and later in another part do:
- (void)yourMethod:(id)sender{
//download the info but do not update the GUI
[self performSelectorOnMainThread:#selector(updatingTheGUI:) withObject:yourObject waitUntilDone:NO]
}
- (void)updatingTheGUI:(id)sender{
//Update your GUI
}
You will notice that the viewDidLoad method documentation of UIViewController states:
...Called after the controller’s view is loaded into memory.
This doesn't necessarily mean that it's called after the view is displayed on screen.
To answer your other questions, if you make your network request the way you have described, no, animations will not continue playing while the request is in progress and no, you can't guarantee that the UI will be responsive. This is because the network request will take an unknown amount of time. Therefore, if you make the request on the main thread, the main thread will be blocked for that period of time, however long it takes.
And, as for the last question, is multithreading the way to go? As others have stated, the easiest and probably most popular way of handling this is to initialize the NSURLConnection with initWithRequest:delegate:. The delegate being your UIViewController or Connect class, or whatever class you want to conform to the NSURLConnectionDelegate protocol and use the NSURLConnectionDelegate methods to process the downloaded data. NSURLConnection will do the work asynchronously and keep the main thread free to handle animations, displaying the UI, etc.
I know it sounds a bad idea for your app. performance but try giving a delay or sleep in between to check if it works that way. Later try to implement the asynchronous call as someone earlier stated..

ASIHTTP - Leave the view and get EXC_BAD_ACCESS

I'm running an http request with my view controller as the delegate using ASIHTTP, if the user leaves the view controller before the request is done, I get a EXC_BAD_ACCESS in the ASIHTTP code that is doing an if ( delegate ... )
Is there something I have to do in my view controller dealloc? I want to simply drop the request if the user leaves.
The most common reason for problems like this is an ASIHTTPRequest object that is still active and where its delegate field points at an object that is now destroyed. The EXC_BAD_ACCESS will then happen when ASIHTTPRequest tries to tell the delegate the request has finished and so on.
Your dealloc method for the object that is the asihttprequest delegate should have:
request.delegate = nil;
[request cancel]
[request release]
If you have only one request active at a time, you must have this same code anywhere you start a new request, to ensure that any previous request is correctly cancelled.
If you have more than one request active, you will need to keep track of all of them, and cancel and nil the delegate of all of them.
For reference, I've been using ASIHTTPRequest for ~18 months, and have contributed back a number of fixes for difficult race conditions related to cancelling requests.

ASIHTTPRequest Problem

Is anyone else having this problem with ASIHTTPRequest? It seems that when I perform an async request from within a background thread with delegate set to the instance I can run into trouble as the delegate can be freed before the request (which is put into an NSOperationQueue) returns a callback.
It seems that ASIHTTPRequest doesn't retain it's delegate - on the other hand Apple's NSURLConnection does retain the delegate ("NSURLConnection retains its delegate when it is initialized. It releases the delegate when the connection finishes loading, fails, or is canceled.").
Should I make sure to perform synchronous ASIHTTPRequests in background threads to make this work (instead of async requests)? Or maybe I should dump ASIHTTPRequest? Or am I just crazy?
Assuming you're using a very recent version of ASIHTTPRequest, the correct way to work with it (and avoid crashes) is that:
The delegate should retain the request (and the request should not retain the delegate)
The delegate should do the following when the delegate is destroyed (or when you want to cancel the request):
[request setDelegate:nil];
[request cancel];
[request release];
You shouldn't get any crashes this way. (I rewrote the delegate handling in ASIHTTPRequest a few months ago exactly to avoid some of these issues, and I checked with the folks from Apple that this was a correct way to handle things before doing so. My changes are all in the official ASIHTTPRequest repository on github, though there hasn't been an official release since - ie. these changes aren't in the v1.7 release, so with v1.7 or earlier you could still see crashes when following the above advice.)
I don't know if ASIHTTPRequests doesn't retain the object, but did you try to retain it when performing and the releasing it at the end ?
I never had a problem yet with this very good wrapper ^^

NSURLConnection and XCode "Build and Analyze"

I have been following the documentation for using NSURLConnection and am using delegates for managing the connection (overriding among other methods connection: didFailWithError: and connectionDidFinishLoading:). The memory allocated for the NSURLConnection object is released in those delegate methods.
My problem is that running "Build/Build and Analyze" from the XCode menu triggers an annoying "Potential leak of an object (...)" warning in the method calling alloc on the NSURLConnection class (logically enough as I do not release it in the same block).
Is there a way to silence this warning?
The way round this to store the connection object in a retained instance variable. Then you can safely release it at the end of your first method.
When you click on the Analyzer message it gives you more information, showing the path of execution etc. Does this indicate that it's just being dumb or does it give you a hint as to what might be wrong?
Assuming the analyzer is just being dumb, I'd just keep the reference in an ivar if the message bothers you, then release it from within -dealloc.
Releasing an allocated resource from within a delegate method feels a bit voodoo to me in any case. I'd even be tempted to move all of the code that's processing the NSURLConnection into its own class.
I'm with Florent on this one. "Listing 1" in Apple's documentation on NSURLConnection will flag a "potential leak" in "build and analyze". Yet putting a [theConnection release] at the end of the method crashes.
Would love to have someone comment on this for me!

Stop lazy-loading images?

Here's the issue – I followed along with the Apple lazy-load image sample code to handle my graphical tables. It works great. However, my lazy-load image tables are being stacked within a navigation controller so that you can move in and out of menus. While all this works, I'm getting a persistent crash when I move into a table then move immediately back out of it using the "back" button. This appears to be a result of the network connections loading content not being closed properly, or calling back to their released delegates. Now, I've tried working through this and carefully setting all delegates to nil and calling close on all open network connections before releasing a menu. However, I'm still getting the error. Also – short posting my entire application code into this post, I can't really post a specific code snippet that illustrates this situation.
I wonder if anyone has ideas for tasks that I may be missing? Do I need to do anything to close a network connection other than closing it and setting it to nil? Also, I'm new to debugging tools – can anyone suggest a good tool to use to watch network connections and see what's causing them to fail?
Thanks!
Have you run it through the debugger (Cmd-Y)? Does it stop at the place where the crash is happening? That should show you in code where the issue is happening. I'm betting the issue has to do with over-releasing something rather than cleaning up connections. Are you getting EXC_BAD_ACCESS? Check any delegates and make sure they are nil when -viewWillDisappear gets called. That way, if anything tries to call back to a delegate, it will just be a no-op.
You may also want to try enabling zombies (NSZombieEnabled) which will tell you when an object that has been released is being accessed again. It's very helpful in finding over-released objects.
Ah ha... after a large zombie hunt (thanks, Matt Long), I discovered that the issue stems from an error in Apple's LazyTableImages sample code. That example provides the following implementation for canceling all image loads, which I turned into a general-purpose stopAllImageLoads method...
From RootViewController.m in LazyTableImages sample code:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// terminate all pending download connections
NSArray *allDownloads = [self.imageDownloadsInProgress allValues];
[allDownloads performSelector:#selector(cancelDownload)];
}
There is in error in the last line of the above method where performSelector is called on an array of objects. The above implementation calls the selector on the array itself, rather that on each object in the array. Therefore, that last line should be this:
[allDownloads makeObjectsPerformSelector:#selector(cancelDownload)];
Once that line was changed, everything else fell into place. It turns out I wasn't calling my stopAllImageLoads method where I meant to – I had disabled it at one point because it was causing an error. Once that was back in place, the memory issues cleared up because image loads were successfully canceled before the table delegate was released.
Thanks all for your help.
If you're doing ANY asynchronous function (network requests, Core Location updates, etc), you run the risk that your view controller that is the delegate of that action is deallocated by the time the async function returns. i.e. you back out of the view and take the delegate target away from the background process. I've dealt with this several times.
Here's what you do. Use the ASIHTTPRequest library (which you should be doing anyway--it's brilliant). Create a synthesized property to hold your request. Then in viewWillDisappear, call -cancel on your request. To be safe, I also set its delegate to nil, but that should be unnecessary.
Here's a sketch of what you want to do. Note I typed this right here, haven't syntax-checked it or anything.
#implementation MyViewController
#synthesize req //this is an ASIHTTPRequest *req.
-(void)viewDidLoad
{
//make an NSURL object called myURL
self.req = [ASIHTTPRequest requestWithURL:myURL];
self.req.delegate = self;
[self.req startAsynchronous];
}
-(void)viewWillDisappear
{
[self.req cancel];
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSString *string = [request responseString];
}