GCD with NSURLConnection

I use GCD to send an HTTP request asynchronously. Here is the code that does not work:

 dispatch_async(connectionQueue, ^{ NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; [request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [connection start];//Not working }); 

the above code does not work at all. I do not get any call in NSURLConnectionDelegate methods.

But when I tried the following code, everything worked fine and I had the correct callbacks

 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; [request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; dispatch_async(connectionQueue, ^{ [connection start]; // working fine. But WHY ???? }); 

Can someone explain this weird block / GCD behavior?

+7
source share
4 answers

Try this in the first part of your code -

 dispatch_async(dispatch_get_main_queue(), ^(void){ NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [connection start]; } 

If you establish a connection in the background, it will be deleted after the queue has ended, and thus you will not receive delegate calls back. The connection may be in the main queue, so it remains in the main start loop for callbacks. Or you can create your own runloop that will handle your background operation for you, as others have suggested.

+2
source

When using NSURLConnection for asynchronous communication, you need the thread that it was created to connect to its RunLoop connection to have this thread polling for the same connection delegation method.

The correct way to create an NSURLConnection is asynchronous without relying on the RunLoop of the main thread below:

 // Done within a Grand Central Dispatch block, or NSOperation. // We do not start the connection here because we still need to attach the connection to the RunLoop of the current thread and handle how it will communicate responses back to the caller. theConnection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO]; // A previously instantiated NSOperationQueue for managing the delegate callbacks if-and-when they occur. // [theConnection setDelegateQueue:delegateQueue]; /* // Other NSURLConnection logic, etc. */ // We start the connection manually after properly establishing how it will poll and respond to events. [theConnection start]; // This is for the RunLoop of the current thread. (Only needed for < iOS 6 compatibility) // If this method is executed inside a GCD block or NSOperation, it will be the RunLoop of the thread run in-parallel to the Main Thread. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]]; // From here, you can alter the time interval to coincide with a specific "time-out" event you'd like to occur. 

Where "TheConnection" is a member variable of the current class of type "NSURLConnection". In addition, you will need to create an NSOperationQueue member variable to manage delegate callbacks as soon as your connection receives a response. These calls will be transferred asynchronously back to the thread on which the connection is being made.

From there, you can return data using the appropriate delegate methods of NSURLConnection.

The advantage of using Grand Central Dispatch or operation queues for your threads is that Threading and RunLoop mechanics are already built-in; You do not have to manually allocate an extra thread with your own RunLoop inside it. This eliminates the two-step redundancy of creating a background thread for managing asynchronous server calls.

I hope this is enough for you to create a truly asynchronous network model for your application on the right path. :)

+2
source

NSURLConnection will always fetch data in the stream that was created (alloc init). This explains why it will work differently. The first method works, but the thread dies before you can get any information from NSURLConnection. NSURLConnection already allows asynchronous loading, but if you want to process data asynchronously, you should use the following method:

 + (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler 

There are some limitations to a method such as authentication, and you cannot keep track of which part of the document has been downloaded to date. You must also specify the NSOperationQueue created, the default queue is the main loop queue.

+1
source

You can run a temporary NSOperationQueue for this connection. This line will live only as long as it is required. Basically, NSOperationQueue ensures that you call delegate calls and process them by rotating the thread to handle each delegate callback. (In most cases, this is the same background thread that goes into standby mode and resumes when new data is downloaded or when the connection fails, connection is terminated, etc.). Once you set this queue, delegate calls will begin to flow into your application.

 dispatch_async(connectionQueue, ^{ connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; NSOperationQueue __autoreleasing *tempQueue = [[NSOperationQueue alloc] init]; [connection setDelegateQueue:tempQueue]; [connection start]; }); 

If you choose RunLoop, running runloop instead will be an additional burden on your part.

+1
source

All Articles