minus
minus

Reputation: 320

NSURLConnection connectionDidFinishLoading Not Called Randomly

I have an iPad application where I am making asynchronous calls using NSURLConnection. In some cases I receive all the response data in connection:didReceiveData:, but connectionDidFinishLoading is never called. There is no error. This is somewhat random, because the same responses do complete at other times.

The way that my class works is that around 20 requests are sent in a row using:

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];

And then I just wait for them to come back. Is this a valid way to create multiple requests?

Here is a sample header of a response that did not complete. It is indistinguishable from the header of a response that did complete.

>Keep-Alive: timeout=5, max=100
>Transfer-Encoding: Identity
>Server: Apache/2.2.9 (Unix) mod_ssl/2.2.9 OpenSSL/0.9.8b mod_jk/1.2.26
>Content-Type: application/json;charset=UTF-8
>Connection: Keep-Alive
>Date: Wed, 03 Apr 2013 05:25:32 GMT
>Cache-Control: private, no-transform, max-age=600

One weird symptom of the problem is that I started checking the expected content length using:

long long download_size =[response expectedContentLength];

The Content-Length is never set in the http header. When a request fails the download_size is -1 (expected), when the same request does not fail the download_size is set to some number. However, there are many cases where the download_size is not set and the response does not fail.

Upvotes: 1

Views: 2156

Answers (2)

Rob
Rob

Reputation: 437862

This is not a great way to initiate 20 requests because:

  1. The requests operate concurrently, and thus if you don't have some class that encapsulates all of the response data and the like from the individual requests, your app can be confusing the response data from various requests;

  2. Because they happen concurrently and because iOS only allows five concurrent requests to a given server, it will delay (and possibly timeout) the others.

You have a bunch of different approaches, but you probably:

  • You might want to be doing this network stuff on a background queue;

  • If you want concurrent operations (and there's an observable performance benefit from doing so), you could use NSOperationQueue to have concurrent operations, but limit how many concurrent operations are ongoing (to 4 or 5) with the use of maxConcurrentOperationCount. This process is trivial if you're using synchronous network operations in these background operations, but surprisingly complicated if you use asynchronous network operations with your own delegate methods in the background queue, though.

  • If you really need (a) to use the asynchronous network calls using your own delegate methods (e.g. updating progress views, streaming protocols, etc.); and (b) you want to enjoy concurrent operations, it will be considerably easier to use AFNetworking than it will be to write your own. I went through this exercise of writing my own, but having done that exercise once, I now more fully appreciate what AFNetworking brings to the table.

Sure, you could get around all of this by managing your own array of pending network requests, initiate the first one, and have the connectionDidFinishLoading and/or didFailWithError kick off the next one in your queue, but you lose the performance gain of concurrency. But it is one simple work-around.

Upvotes: 1

matt
matt

Reputation: 535526

I do hope you are use separate objects here. You are saying

NSURLConnection *connection = 
  [[NSURLConnection alloc] initWithRequest:request 
     delegate:self startImmediately:YES];

Well, I sure hope those are 20 different self objects. You must not expect this to work with a single object acting as the delegate for all 20 requests simultaneously!

Upvotes: 1

Related Questions