Reusing an instance of NSURLConnection - iphone

I'm using an instance of NSURLConnection on the iPhone to request data from a server, managed by a delegate as usual. The requests are quite frequent (maybe once every 2 minutes say) and have a common and fixed URL. Rather than seeing the good instance of NSURLConnection being released after each download and then a new one being created:
Is there any worth in retaining the first connection and reusing it? (I'd hope so, one good authentication should be worth a thousand.)
If so, how do I reuse it? The standout method in the docs is -start but this seems to crash the app when called on an already used (and non-nil) instance of NSURLConnection. [The docs do say -start "causes the receiver to begin loading data, if it has not already."]
In case it's of help with regard to the above questions, I am (was!) proposing:
if (connection_ == nil)
{
connection_ = [NSURLConnection connectionWithRequest:request
delegate:self];
}
else
{
[connection_ start];
}

The docs seems to say that the URL connection retains it's delegate (unconventional, but necessary in this case) and then releases it when the connection finishes loading, fails or is cancelled.
The problem is that the delegate isn't a settable property on NSURLConnection and so you can't reset it after it's been released. This pretty much renders the URL connection useless after it has run once, requiring you to release and recreate it if you want to do it again.

Related

How to Make Web Service Call Without Making the GUI Unresponsive

I have a UISearchBar and UISearchDisplayController. When the user writes text in it inside searchBar:textDidChange: I make a web-service call to filter my TableView. The problem is that the GUI get unresponsive until I get the result from the web-service. I've tried to solve it using [self performSelector:#selector(callWebService:) withObject:searchText];, but it's still unresponsive.
EDIT: Following Flink advice, I changed performSelector to performSelectorInBackground, but now the tableView doesn't filter correctly, it only show 'No Results'.
even tableView:numberOfRowsInSection: isn't get called.
EDIT Again: The reason I got 'No Results' was due to not calling reloadData on the correct tableView. UISearchDisplayController has a property named searchResultsTableView. So in the end what I used was [self.searchDisplayController.searchResultsTableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:false]; and now it works fine.
It should be noted that although I chose the performSelectorInBackground, I probably should have tried to use sendAsynchronousRequest method on NSURLConnection - See AliSoftware's answer.
You need to make your web call async.
http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial
In your case, you can change performSelector to performSelectorInBackground
You should avoid creating a background queue or thread to perform your network request(which is what performSelectorInBackground: does) as this creates a worker thread just for this which is not as efficient as scheduling the request on the NSRunLoop.
Dedication a thread will make the processor activate the thread regularly to check if there are some data, and creating a thread for that is quite overkill. Scheduling the request on the run loop (as a run loop source) will use network interruptions to signal incoming data from the socket and thus will only be activated when there is actual data available.
To do this, simply use the asynchronous methods provided by NSURLConnection.
One solution is to use the delegate approach provided by NSURLConnection (this is the old way to do it, the only way that was available in the NSURLConnection API back in iOs3)
Another more modern solution is to use the block API provided by NSURLConnection which is easier to use and code.
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse* response, NSData* receivedData, NSError* error)
{
// Your code to execute when the network request has completed
// and returned a response (or timed out or encountered an error)
// This code will execute asynchronously only once the whole data is available
// (the rest of the code in the main thread won't be blocked waiting for it)
}];
// After this line of code, the request is executed in the background (scheduled on the run loop)
// and the rest of the code will continue: the main thread will not be frozen during the request.
Read more in the URL Loading System Programming Guide and in the NSURLConnection class reference.

Using multiple NSURLConnections at the same time - best practices

In my iPhone app, I've been able to use NSURLConnection properly to download data from a URL. I simply set the delegate to my UIView, and make sure that I set up the UIView to answer for the proper delegate functions such as -connection:didReceiveResponse:. However, if I have a number of NSURLConnections (either for a similar type of request, or multiple kinds of requests), it gets messy because the delegate functions, such as didReceiveRequest, don't differentiate between the different requests. The advantage of asynchronous requests is that you are supposed to be able to multiple at once, without blocking the main thread. What's the best practice for how to use multiple NSURLConnection requests at the same time?
I prefer to wrap them in a higher-level object (like ImageDownloader or SomeWebServiceCall) that has all the per-connection/call state information. I usually create a delegate these objects so that the caller gets a more specific callback when the operation has succeeded or failed.
Perhaps look into ASIHTTPRequest, instead of NSURLConnection. ASIHTTPRequest makes a lot of this work trivially easy.
In this case, I'd say NSOperation is your best bet. ASIHTTPRequest is based on NSOperation and allows you to handle each request as an operation, which serves as the delegate for its own NSURLConnection.
You'll have to be careful here though, because by default NSOperations are run on separate threads, but some APIs, like this one, are required to be executed on the main thread. If you inspect the source code to ASIHTTPRequest you'll notice they've got some infrastructure to ensure delegate methods are called on the main thread.
Create an instance variable for each NSURLConnection and NSMutableData. All of your delegate methods have the NSURLConnection object as an argument, so you can match them as such:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == aConnection) {
// Do something with the data for aConnection
} else if (connection == otherConnection) {
// Do something with the data for otherConnection
}
}
This still limits you to one connection per instance variable, so it's not suitable for, for instance, a table view with images on each row that need to be loaded.

Memory management for NSURLConnection

Sorry if this has been asked before, but I'm wondering what the best memory management practice is for NSURLConnection. Apple's sample code uses -[NSURLConnection initWithRequest:delegate:] in one method and then releases in connection:didFailWithError: or connectionDidFinishLoading:, but this spits out a bunch of analyzer warnings and seems sort of dangerous (what if neither of those methods is called?).
I've been autoreleasing (using +[NSURLConnection connectionWithRequest:delegate:]), which seems cleaner, but I'm wondering--in this case, is it ever possible for the NSURLConnection to be released before the connection has closed (for instance, when downloading a large file)?
This returns autoreleased NSURLConnection:
+[NSURLConnection connectionWithRequest:delegate:]
If you want to keep the reference you need to retain it. Once you are done then release it.
It does not help to autorelease already autoreleased object.
I assume the example code will somewhere retain the NSURLConnection and then release it when the connection fails, as shown in your example.
This returns allocated object that you have to take care of cleaning
-[NSURLConnection initWithRequest:delegate:]
Because the method is named init, the other one above does not have init in the name or copy so you don't have to worry about the memory management.
If your object internally creates NSURLConnection at some point and then releases it when connection is done or failed you should reset the reference to nsurlconnection to nil.
In your dealloc you should cleanup the NSURLConnection as well, if it is nil nothing will happen but if it is still allocated it will clean it up.
See apple doc about memory management - it's quite simple.

iphone: applicationWillTerminate not called during a NSURLRequest?

I am working on an application where the user at some point must wait for a proper response from a webservice. This might take some time, because it requires a manual confirmation from the webservice. Because of this, the timeoutInterval on the request is set very high to prevent it from terminating too early.
The problem comes when starting the application immediately after the program has returned to the home screen. The application will not start (black screen), and I think it is because the request hasn't been released and is still waiting for a response (I might be wrong though).
I tried the applicationWillTerminate method, but it isn't called when pressing the home button. Again, this might be because the application is still waiting for a response, but a better explanation would be greatly appreciated :)
Also, does someone have any idea on what to do?
code:
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:urlAdress]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10000];
NSHTTPURLResponse* response = nil;
NSError* error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse:&response
error:&error];
Making a synchronous request on the main thread will block the entire run loop until you receive a response or the request times out. Nothing will get called during this time, including applicationWillTerminate. It is a very bad idea to do this. You should either detach a separate thread for the request or create an asynchronous NSURLConnection.
That being said, the OS will free all memory anyway when it terminates your app so there is little chance that anything from the last launch will be "left over" upon relaunch. Unless your server is blocked because it lost its client when your app terminated. In which case you have to write your server in a way that can handle such a case because it can happen anytime.
If the applicationWillTerminate delegate method is not called,
Then I guess the following delegate method would be getting called.
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(#"Application Did Resign Active");
}

Making GET and POST Requests from an iPhone Application - Clarification Needed

I'm following along with this useful looking answer to my question.
It seems to be working, but here are two questions I have:
How do I detect an HTTP error? The didFailWithError method doesn't seem to be getting called?
UPDATE: It considers any response a success. So I guess I have to handle HTTP errors in the didRecieveResponse method, but besides telling the user there was an error when I hit an HTTP error, do I need stop the connection somehow? And/or cleanup?
I see this line in the answer:
[[NSURLConnection alloc] initWithRequest:request delegate:self];
Do I need to release that? Where, how, when?
You will get the status code returned in the didReceiveResponse
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSHTTPURLResponse *httpResponse;
httpResponse = (NSHTTPURLResponse *)response;
int statusCode = [httpResponse statusCode];
//statusCode will be the http code returned like 201,500
}
For stopping the connection, use a class-level variable for the connection. The best way to go about it would be create a wrapper which sends requests and receives response. Make your viewcontroller a delegate of this class and whenever the didReceiveResponse gives an error status code, call the appropriate method of the delegate and stop the connection.
Here's a good wrapper class example
http://kosmaczewski.net/projects/objective-c-rest-client/
Yes, you need to release that object. See the Memory Management Programming Guide for Cocoa. Basically, if you ever create an object with a method name that begins with alloc or new or contains copy, you become an owner of the object and are responsible for freeing it later. Only in the case where you know you're going to need the object up until program termination is it ok not to free it, in which case the operating system reclaims the memory when your app terminates.
If you only need the object within a small scope, you can send it the autorelease message. This will add it to the autorelease pool. The autorelease pool periodically sends a release message to each object in it. It's kind of complicated; see the section on autorelease pools. For example:
In this case, though, because NSURLConnections are asynchronous, autoreleasing won't work. You don't know exactly when it's going to call back into your object with data, so you want to make sure the object hasn't been released yet. The only way to be sure is to know exactly when you're done with the object, and then send it a release message yourself.
All of the various init* functions return a pointer to the given object, so you can just do:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
...
// when done with connection:
[connection release];
Answering the question in your "update"...
Immediately autorelease the NSURLConnection. The connection object is retained by the NSRunLoop (to which it adds itself automatically unless you use the startImmediately:NO constructor). It will get automatically removed from run loop (and hence dealloc'd) on error or finish.