Reputation: 101
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
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
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
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
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