Fifth NSURLConnection's delegate not called - ios

I am using NSURLConnection in my app for downloading. It is working fine.When i start the connection the delegates methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse*)response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
get called.
But when the number of connections become more then 4 then for the 5th connection the delegate methods do not called until the delegate method "connectionDidFinishLoading" is called for any of first four connections (means one of any four connection has finished). Then the delegates (didReceiveResponse and didReceiveData ) of Fifth connection start getting called .In short my NSURLConnection is delegate methods only called for 4 connections at a time.
I just want to know is there any way that i can increase this limit so that more then 4 (may be 8 or 9) connection's delegates get called at a time ? Is this a limit by iOS or something is wrong with my app.

Check http://blog.lightstreamer.com/2013/01/on-ios-url-connection-parallelism-and.html
iOS has a limit on the number of concurrent NSURLConnections to
the same end-point? No? Maybe you should, we discovered it the hard way.
They also offer a thread pool implementation to get around this limit.

I think you should schedule these connections to a manual created queue other than the default one, so as to avoid the potential affect. It works well for me. Here are the sample code you can try:
NSURLRequest *request; // a URL request
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // manual create queue to schedule your URL connections
queue.maxConcurrentOperationCount = 10; // configure the max count
// by specifying NO for the #startImmediately# parameter,
// you can schedule an connection on the queue you like before it starts.
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection setDelegateQueue:queue];
[connection start];
Hope this will be helpful.

You can't fix it with NSURLConnection (practically), but if you use NSURLSession instead, you can trivially solve the problem.
The NSURLSession API lets you control the maximum number of concurrent requests per server. Just set the HTTPMaximumConnectionsPerHost property on the session configuration object, then create a session based on that configuration, then create tasks within that session much like you would create NSURLConnection objects.

Related

how to make NSURLConnection's delegate protocol method called in this NSOperation instance?

hello i have done this QCM in an iOS test. my choice is B. and it's wrong. i hope someone could help me understand. Thank you in advance.
Here is the question:
NSURLConnection instance created and started inside the implementation of "start" method of the concurrent NSOperation.
What should be changed to make NSURLConnection's delegate protocol method call in this NSOperation instance?
-(void)start{
...
_connection = [[NSURLConnection alloc] initWithRequest: request delegate:self startImmediately:NO];
[_connection scheduleInRunloop: [NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[_connection start];
... }
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Called"); }
there are three answers provide:
A: Set startImmediately to YES
B: Wrap this code to dispatch_async(dispatch_get_current_queue(),^{});
C: Nothing, didReceiveResponse will be called.
No change is necessary to this code sample (option C).
Setting startImmediately to YES (option A) would merely start the connection prior to scheduling it on the appropriate run loop, which would cause problems.
You could, theoretically dispatch the creation of the connection to the main thread (option B), thus eliminating the need to manually schedule it on the main run loop. But your code sample (manually scheduling it on the main run loop) accomplishes the same thing and is the more common pattern.
Bottom line, you existing code sample is fine.

AFHttpRequestOperation inside NSOperation

Been doing iOS for a while, but now I'm working in a complex client-server app that requires me to:
Send a request to the server (using AFHttpRequestOperation)
The response is either an interval or a Dictionary
If I get an interval, I need to wait (interval)-seconds and call again the server again (wait / sleep?)
this continues until I receive an expired message, the user cancels or a Dictionary (as mentioned above) is received.
IF a dictionary is received, I have to pause the calling to the server until the user takes an action on the information I display to him.
Based on user decision, I need to resume and keep calling, wait, calling, and so on until i do receive the dictionary or it expires (if the user rejected the information) OR if the user accepts, stop asking and further process the information (but I wont keep calling the serve)
Being new to threading in iOS, what's the best approach:? I have been working with dispatch_asynch + performSelector:onThread:withObject but it's giving me all sort of problems!
Question is: What's the best approach / approach to achieve pause, cancel, wait / sleep in an NSOpertation subclass that internally uses an AFHttpRequestOperation?
I'm a rookie at threading, specially under iOS, so your help is greatly appreciated!!!!
I would suggest not to use threading with networking . While it doesn’t seem clear enough but implementing a asynchronous network requests take very less CPU time.
Many try to do this and end up with delegates methods of an asynchronous network calls not being called. If network calls are performed on a background thread, the thread might probably exit before the delegates are finished calling.
The asynchronous calls can be cancelled just by calling the cancel/pause method. If you feel that main thread is very slow, make sure you avoid heavy operation on it, like parsing, calling CameraView. I used NSrunLoop for just testing how to do this, without much advantages
-(void)start
{
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURL* url = [[NSURL alloc] initWithString:#"http:your url"];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; // ivar
[request release];
[url release];
// Via the run loop
NSPort* port = [NSPort port];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:port forMode:NSDefaultRunLoopMode];
[connection scheduleInRunLoop: runLoop forMode:NSDefaultRunLoopMode];
[connection start];
[runLoop run];
}
You can replace the native api's with your library ones. Hope this helps in some way.

Difference between cancelling a connection and setting it to nil

In iOS I am using NSURLConnection -
NSURLConnection *aConnection = [[NSURLConnection alloc....
is there a difference between:
[aConnection cancel];
and
aConnection = nil;
Thanks
When you send the connection a cancel message, it will stop to invoke your delegates as soon as possible and tear down the connection.
Note: it may happen in rare cases that you still get one already queued delegate message other than connection:didFailWithError: after you send cancel from a different tread than the one the connection schedules delegates.
With setting your reference to the connection to nil, you simply do this. This does not cancel the connection - and if this was your only reference, you also can't send a cancel anymore. ;)
Basically by doing this:
aConnection = nil;
you cannot be sure that there are no other references to this object. So you should do this:
[aConnection cancel];

Making threaded NSURLConnections

I have a lot of connections going when my app starts, so I wanna put them on background threads so I can make new connections other than the starting connections before they all complete.
Below, threadedRequest: is a method that's starting a NSURLConnection, but when I call performSelectorInBackground:withObject: in the if clause, the connection starts, but never finishes. The else clause works fine and returns data from the connection
if (background)
{
[self performSelectorInBackground: #selector(threadedRequest:) withObject: args];
}
else
{
[self performSelector: #selector(threadedRequest:) onThread: [NSThread mainThread] withObject: args waitUntilDone: NO];
}
If you want to perform NSURLConnection in the background, you must be sensitive to special conditions that apply to NSURLConnectionDataDelegate methods in background threads. You have several options:
Use one of the non-delegate alternatives. Given that you're performing this in the background, you could use sendSynchronousRequest, or if just requesting data from a simple URL, you could use NSData class method dataWithURL.
If you really need the delegate version (because you need progress updates via didReceiveData or because you need one of the NSURLConnectDelegate methods for authentication or the like, you have a couple of basic options:
You can create a NSOperationQueue and set the delegate queue for the connection. For example, rather than:
- (void)startConnection:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
You could do:
- (void)startConnection:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection setDelegateQueue:self.connectionDelegateQueue];
[connection start];
}
Alternatively, you could do what AFNetworking does (see AFURLConnectionOperation.m, for sample implementation):
Create a dedicated NSThread for the NSURLConnectionDataDelegate calls;
Start a NSRunLoop on that thread;
Use the same startImmediately:NO rendition of the NSURLConnection init method as above; and
Use the scheduleInRunLoop option for the NSURLConnection before you start it.
Or, easiest, you could just use AFNetworking
Two observations:
I might be inclined to use operation or dispatch queues instead of performSelectorInBackground. See Concurrency Programming Guide.
By the way, you should be aware that there is a limit as to how many concurrent NSURLConnection requests iOS can perform simultaneously (it's 5 or 6, I believe). Thus, if you initiate a dozen background requests and then initiate a "foreground" request, the "foreground" request may not start immediately. You may want to limit how many background requests that can operate concurrently if you want to avoid this potential problem.
Personally, when I want to constrain the number of network requests, I add my background requests to a NSOperationQueue and set the number of maximum concurrent operations (e.g. I generally use 4), enjoying the benefits of concurrent operations, while not trying to perform more simultaneous connections than iOS will permit. By the way, when availing yourself of operation queue's maxConcurrentOperations feature, you have to make sure that the requests are synchronous (or wrap the asynchronous connection in a custom NSOperation that won't terminate until the connection is done or fails).

Using NSURLConnection Multiple times

I am trying to start a second NSURLConnection after starting the first one. My first one works perfectly: the appropriate delegates are all called, and everything executes as planned. However, after the first one finishes, I create a second one, and nothing happens (the delegate is never called). What could be happening? I know I can't reuse the same NSURLConnection, but I reinitialise it before using it again, so it should be a completely new connection.
Here's my code for starting (both) connections. It's the same instance variable, but it's reinitialised. Also note that the second one is not started until the first one has finished running completely.
if (connection) {
[connection cancel];
}
currentResponse = nil;
error = nil;
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
if (!connection) {
NSLog(#"Connection could not be initialized.");
[self connectionFinished];
} else {
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
}
You haven't shared how you're running it "on a different thread", but the typical problem is if you are using dispatch queues or operation queues, your connection is running asynchronously, itself, and therefore the dispatched operation is completing and getting released and you're losing your connection.
A couple of possible solutions:
You can perform your network operations synchronously in this background operation of yours (this is the only time you should do synchronous network operations). This is the simplest solution, though you haven't explained what you're doing with your NSURLConnection, so this technique may or may not work for you. But if you're just trying to download something from a URL, you can do:
NSData *data = [NSData dataWithContentsOfURL:url
options:0
error:&error];
This approach doesn't work if you're doing any a little more complicated that requires the NSURLConnectionDataDelegate methods, such challenge-response authentication or if you are streaming using didReceiveData to reduce your app's memory footprint or for performances reasons, etc. But if you're just trying to download data from a remote server (e.g. retrieving an image, an XML/JSON feed, etc.), this is easiest.
In a similar vein (i.e. you don't need the NSURLConnectionDataDelegate methods), but you're you're creating some rich NSURLRequest for your connection, then you can use either sendAsynchronousRequest or sendSynchronousRequest.
If you need the NSURLConnectionDataDelegate calls, you can use the setDelegateQueue (designating a NSOperationQueue) or scheduleInRunLoop (designating a NSRunLoop), and it will automatically dispatch the connection updates to the appropriate queue/runloop. Just make sure to initWithRequest using the startImmediately option of NO, set the delegate queue or runloop, and then start the connection. With this technique, you preserve the full richness of NSURLConnectionDataDelegate if you absolutely need it.
Alternatively, if you're not using operation queues, you could also keep your background operation alive until the connection is done. This approach gives you synchronous behavior in you background operation (keeping your connection alive), while preserving the NSURLConnectionDataDelegate methods. This technique is demonstrated by Apple's XMLPerformance Sample (see the downloadAndParse method of CocoaXMLParser.m and LibXMLParser.m), where they initiate the NSURLConnection and then use the following construct to keep the background operation alive until the NSURLConnectionDataDelegate methods end up setting the done instance variable:
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
} while (!done);
I confess that I find this final approach vaguely dissatisfying and would lean towards the other alternatives, depending upon what flexibility and functionality you need from your NSURLConnection. For us to provide more meaningful counsel, you just need to provide more information about (a) the sort of work you're doing in your NSURLConnectionDataDelegate methods; and (b) which technology you're using to run your code in the background.
For a few more options, also feel free to see GCD and async NSURLConnection.

Resources