2

I have a method foo: that is called on a background thread. This method simply sends a request to a server, and, after data are retrieved, performs some calculations about those data and returns. In this case I prefer to use sendSynchronousRequest: because this method is convenient and it doesn't matter if the thread is blocked. However, the response contains a "Location" header field that will redirect to another page. I want to read the response to get those "Set-Cookie" header fields before redirection. It seems that the synchronous method does not allow me to.

I tried to use the asynchronous one and implement a NSURLConnectionDataDelegate, but the thread is finished before those methods of the delegate is called. (I suppose the way that Apple implements the asynchronous one is to perform those time-consuming works on a new thread)

Is there any way to solve this problem? Since performing an asynchronous request on the main thread may add complexity to my program.

The foo: method is kind of like this

- (Result *)foo
{
    NSURLMutableRequest * request = blablabla;
    //Do something to initialize the request
    NSData *data = [NSURLConnection sendSynchronousRequest:request 
                                         returningResponse:&response
                                                     error:&error];

    //Do something with the data

    Result *result = [[Result alloc] init] autorelease];
    //fill the result

    return result;
}
Poligun
  • 101
  • 1
  • 9
  • Not sure why you think that performing an asynchronous request on the main thread may add complexity to your program. Creating background threads usually only makes sense if you have long or computationally intensive tasks to perform, which doesn't seem to be the case here, as the thread finishes quickly. – Andreas Ley Apr 11 '13 at 07:45
  • As you've mentioned, the foo method is a intermediate method to be called in a computationally intensive task that I feel like it should be performed not on the main thread. – Poligun Apr 11 '13 at 08:22

4 Answers4

3

You could use a Grand Central Dispatch semaphore to wait until the asynchronous request returns:

- (Result *)foo
{
    NSMutableURLRequest * request = [[NSMutableURLRequest alloc] init];
    // set request's properties here

    __block Result *result;
    dispatch_semaphore_t holdOn = dispatch_semaphore_create(0);
    [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (error)
        {
            //  handle error
        }
        else
        {
            result = [[Result alloc] initWithData:data];
        }
        dispatch_semaphore_signal(holdOn);
    }];

    dispatch_semaphore_wait(holdOn, DISPATCH_TIME_FOREVER);

    return result;
}

NOTE: This code requires iOS 4.0+ and ARC!

Andreas Ley
  • 8,645
  • 1
  • 42
  • 55
0

Look into [NSCondition] which enables you to wait and signal threads

Basically you allocate a NSCondition and in the block you'll have [condition wait]; which will cause the thread to wait. then, when the async operation is done, you call [condition signal]; which will signal the waiting thread to continue.

http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/NSCondition_class/Reference/Reference.html

Or Arbel
  • 2,866
  • 1
  • 27
  • 40
0

You can create your own NSRunLoop and do your requests there. Stop the run loop once you're done with your requests.

Or if you are lazy like me and don't want to mess with run loops, just put your connection on the main thread:

dispatch_async(dispatch_get_main_queue(), ^(void){
    self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection start];
}
yeesterbunny
  • 1,847
  • 2
  • 13
  • 17
0

You can find a small and simple class that lets you do this on github. It provides two primary objects - a NSOperationsQueue manager and NSOperation subclasses designed to run background fetches. As was mentioned, if all you were doing was fetches, you could do that on the main thread. But if you want to do data processing too, then this project will let you do that in a completed method.

A nice property of the OperationsRunner class is that you can cancel operations at any time (when for instance the user taps the back button), and everything gets torn down quickly with no stalling or leaking.

However, if all you ever do is this one fetch and one process, then you could as others have said just fetch the data on the main thread, and once you have it then dispatch a "processing" block to one of the concurrent system threads, and when that processing is done, dispatch another message to the main thread telling you that the work is complete.

David H
  • 39,114
  • 12
  • 86
  • 125