Poligun
Poligun

Reputation: 101

How to perform an asynchronous request on a background thread?

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;
}

Upvotes: 2

Views: 3237

Answers (4)

David Hoerl
David Hoerl

Reputation: 41622

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.

Upvotes: 0

Andreas Ley
Andreas Ley

Reputation: 9335

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!

Upvotes: 3

yeesterbunny
yeesterbunny

Reputation: 1847

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];
}

Upvotes: 0

Or Arbel
Or Arbel

Reputation: 2975

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

Upvotes: 0

Related Questions