Blocks get fired one after another when using dispatch_group_async - iphone

URLs in the array are called one after another. Should it not be called all at once, like nsoperationqueue? Please help me here, Thanks
- (void) allTasksDone {
NSLog(#"DONE");
}
- (void) callMultiple {
dispatch_queue_t myQueue = dispatch_queue_create("com.mycompany.myqueue", 0);
dispatch_group_t group = dispatch_group_create();
NSArray *urls = [NSArray arrayWithObjects:
#"http://www.a.com",
#"http://www.b.com",
#"http://www.c.com",
nil];
for (NSString *url in urls) {
dispatch_group_async(group, myQueue, ^{
NSLog(url);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSHTTPURLResponse *response = NULL;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSLog(#"COMPLETE");
});
}
dispatch_group_notify(group, myQueue, ^{
[self allTasksDone];
});
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self callMultiple];
[self.window makeKeyAndVisible];
return YES;
}

dispatch_queue_create creates FIFO queue. It takes blocks one-by-one from queue in the same order theme were added. If you would like to perform blocks concurrently you can create different queues for each block or get use of one of global queues.
dispatch_queue_t dispatch_get_global_queue(
long priority,
unsigned long flags);
There are 3 global queues, distinguished by priority.
enum {
DISPATCH_QUEUE_PRIORITY_HIGH = 2,
DISPATCH_QUEUE_PRIORITY_DEFAULT = 0,
DISPATCH_QUEUE_PRIORITY_LOW = -2,
};
Those queues does not wait for previous block completion. So your downloads will be performed concurrently.

First, no, async() does not guarantee asynchronous execution of the blocks. That'll only happen if any given block is blocked waiting for something to happen. GCD will then spin up another thread.
However, if the system is already relatively loaded, GCD isn't going to spin up a new thread to do some work if work is already taking place.
Secondly, there is no reason to push NSURLRequests into the background via GCD. NSURLRequest supports asynchronous downloads already.

Related

Freezing the UI in IOS

The following code is freezing my UI. Cant do any actions.
- (void) longPoll {
//create an autorelease pool for the thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError* error = nil;
NSURLResponse* response = nil;
NSURL* requestUrl = [NSURL URLWithString:#"myurl"];
NSURLRequest* request = [NSURLRequest requestWithURL:requestUrl];
//send the request (will block until a response comes back)
NSData* responseData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self dataReceived:responseData];
});
});
//compose the request
//pass the response on to the handler (can also check for errors here, if you want)
//clear the pool
}
- (void) startPoll {
//not covered in this example: stopping the poll or ensuring that only 1 poll is active at any given time
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
- (void) dataReceived: (NSData*) theData {
//process the response here
NSDictionary *dict=[theData JSONValue];
[self ParseJson:dict];
[self performSelectorInBackground:#selector(longPoll) withObject: nil];
}
Can anyone give me the exact reason for it or any alternative to do the similar code for continues polling.
You are creating an infinite loop:
longCall calls dataReceived calls longCall etc....
What exactly you want to do. There is infinite loop between longPool and dataReceived
there should be mechanism where you stop this call and you can use
#autorelease {} block for create autorelease pool in ARC Enabled project and
NSAutoReleasePool class obj for Without ARC.

NSURLConnection sendAsynchronousRequest: Blocking Main Thread

I'm using NSURLConnection to make multiple asynchronous requests. I'd like to show a progress indicator to show how many requests have been completed out of the total number to be performed. However, when I attempt to set up and display this progress indicator either before making the request, or in another method called before performing the request, it will not show. The progress indicator displays fine when the request is commented out. But when it's not, it's as if Xcode looks ahead and sees an asynchronous request coming and blocks the main thread, thereby making UI changes impossible.
Here's the relevant code being called, including both the request and code to show the progress indicator:
- (void)getRegionalInformationFromChecked:(NSSet *)set atIndex:(NSInteger)index {
__block BOOL responseRecieved = NO;
NSString *stringForURL = [NSString stringWithFormat:#"http://www.thebluealliance.com/api/v1/event/details?event=%#",[[set allObjects] objectAtIndex:index]];
NSURL *url = [NSURL URLWithString:stringForURL];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSLog(#"URL IS GO: %#", stringForURL);
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:queue completionHandler:^(NSURLResponse *_response, NSData *_data, NSError *_error) {
NSLog(#"CHECKED DATA RETURNED AT INDEX %i", index);
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:&error];
if (!_regionalDetails) {
_regionalDetails = [[NSMutableArray alloc] init];
}
[_regionalDetails addObject:dict];
responseRecieved = YES;
}];
regionalSchedulesToGet = [set count];
while (responseRecieved == NO) {}
[[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:[NSString stringWithFormat: #"Getting regional %i of %i", index+2, [set count]]];
if (index+1 < [set count]) {
[self getRegionalInformationFromChecked:set atIndex:index+1];
} else {
[[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:#"Writing to file"];
}
}
When the asynchronous request's block is commented out, the MBProgressHUD displays its value fine. But when the block is inserted, the SDK refuses to update the progress indicator, even after leaving the block (after which any threading issues should have been resolved). It does not update until there are no more requests to display, at which point it reads "Writing to file".
Why does an asynchronous request seem to block the main thread, and why can I not make changes on the main thread immediately before or after the request is called?
With
while (responseRecieved == NO) {}
you block the main thread (probably with almost 100% CPU load) until the asynchronous block has finished. Then you call your
function recursively, start another asynchronous block and block again until that has
finished. Therefore the program control does not return to the main runloop until all
operations have finished. Only then the UI updates are done.
Instead of waiting synchronously (which is always a bad idea),
you should start the next operation at the end of the completion block.
Note also that the queue argument of sendAsynchronousRequest is the queue on which
the completion handler is called, so you can just use [NSOperationQueue mainQueue].
Then your code looks roughly like this:
- (void)getRegionalInformationFromChecked:(NSSet *)set atIndex:(NSInteger)index
{
[[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]]
setLabelText:[NSString stringWithFormat:#"Getting regional %i of %i", index+1, [set count]]];
NSString *stringForURL = [NSString stringWithFormat:#"http://www.thebluealliance.com/api/v1/event/details?event=%#",[[set allObjects] objectAtIndex:index]];
NSURL *url = [NSURL URLWithString:stringForURL];
NSLog(#"URL IS GO: %#", stringForURL);
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *_response, NSData *_data, NSError *_error) {
NSLog(#"CHECKED DATA RETURNED AT INDEX %i", index);
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:&error];
if (!_regionalDetails) {
_regionalDetails = [[NSMutableArray alloc] init];
}
[_regionalDetails addObject:dict];
if (index+1 < [set count]) {
[self getRegionalInformationFromChecked:set atIndex:index+1];
} else {
[[MBProgressHUD HUDForView:[[UIApplication sharedApplication] keyWindow]] setLabelText:#"Writing to file"];
// ... perhaps call a completion function from here ?
}
}];
}
But note that the initial call to getRegionalInformationFromChecked will now
return almost immediately (that's how asynchronous tasks work :-).
Try to dispatch on the main thread all the methods that involve UI refresh

How to run a thread in background of application which will not affect my application user interface

I have made an application for IOS, in which I am using sqlite database, database is local in application.
Now I have given the functionality the application is downloading data from internet & put it in local database & show infront of user. I have given this functionality on - (void)viewDidLoad so that when application is downloading data, it stop working till it finish downloading part, for this user need to wait to interact with application.
Now I wants to functionality a thread run in background of application which will connect the internet & update the application without interfering user.
please help me.
My code of download & save image is this:
-(void)Download_save_images:(NSString *)imagesURLPath :(NSString *)image_name
{
NSMutableString *theString = [NSMutableString string];
// Get an image from the URL below
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:NSURL URLWithString:imagesURLPath]]];
NSLog(#"%f,%f",image.size.width,image.size.height);
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
// If you go to the folder below, you will find those pictures
NSLog(#"%#",docDir);
[theString appendString:#"%#/"];
[theString appendString:image_name];
NSLog(#"%#",theString);
NSLog(#"saving png");
NSString *pngFilePath = [NSString stringWithFormat:theString,docDir];
NSData *data1 = [NSData dataWithData:UIImagePNGRepresentation(image)];
[data1 writeToFile:pngFilePath atomically:YES];
NSLog(#"saving image done");
[image release];
// [theString release];
}
when i am debugging application i seen my application taking more time at below line:
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:NSURL URLWithString:imagesURLPath]]];
You can also use NSOperationQueue and NSBlockOperation if you find GCD difficult.
NSBlockOperation *operation=[[NSBlockOperation alloc] init];
[operation addExecutionBlock:^{
//Your code goes here
}];
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
[queue addOperation:operation];
In GCD You could achieve the same using
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Your Code to download goes here
dispatch_async(dispatch_get_main_queue(), ^{
//your code to update UI if any goes here
});
});
Use either of the API Depending upon your needs. Check this thread which discuss about NSOperationQueue vs GCD for more info.
Questions like these were asked hundreds of times before. I suggest you do a quick search on this. Also there is a topic in apple documentation covering this area thoroughly. here
Basically you can do this with operation queues or dispatch queues. There are some code snippets given above by Avi & Amar.
I'd like to add something since you seem to be unfamiliar with the topic & you mentioned there are web requesting involved.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Dont DIRECTLY request your NSURL Connection in this part because it will never return data to the delegate...
// Remember your network requests like NSURLConnections must be invoked in mainqueue
// So what ever method you're invoking for NSURLConnection it should be on the main queue
dispatch_async(dispatch_get_main_queue(), ^{
// this will run in main thread. update your UI here.
});
});
I've given small example below. you can generalise the idea to match your needs.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// Do your background work here
// Your network request must be on main queue. this can be raw code like this or method. which ever it is same scenario.
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
dispatch_async(dispatch_get_main_queue(), ^{
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *res, NSData *dat, NSError *err)
{
// procress data
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// back ground work after network data received
dispatch_async(dispatch_get_main_queue(), ^{
// finally update UI
});
});
}];
});
});
You can us GCD, like that:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// put here your background code. This will run in background thread.
dispatch_async(dispatch_get_main_queue(), ^{
// this will run in main thread. update your UI here.
});
})
But you need to understand how blocks work for example. Try it.
Use GCD
create your dispatch object
dispatch_queue_t yourDispatch;
yourDispatch=dispatch_queue_create("makeSlideFast",nil);
then use it
dispatch_async(yourDispatch,^{
//your code here
//use this to make uichanges on main thread
dispatch_async(dispatch_get_main_queue(), ^(void) {
});
});
Do ui on main thread only by using
dispatch_async(dispatch_get_main_queue(), ^(void) {
});
Then release it
dispatch_release(yourDispatch);
Here is tutorial
There are a lot of ways:
1 . Grand Central Dispatch
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//Your code
});
2 . NSThread
[NSThread detachNewThreadSelector:#selector(yourMethod:) toTarget:self withObject:parameterArray];
3 . NSObject - performSelectorInBackground
[self performSelectorInBackground:#selector(yourMethod:) withObject:parameterArray];

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

NSOperationQueue and ASIHTTPRequest

I'm writing test cases for a wrapper class written around ASIHTTPRequest. For reasons I can't determine, my test cases complete with failure before the ASIHTTPRequest finishes.
Here's how the program flow works.
Start in my test case.
Init my http engine object, instruct it to create a new list
Create the new ASIHTTPRequest object and set it up.
Add the request to an operation queue.
Wait until that queue is empty
Check to see if my delegate methods were called and fail the test if they weren't.
Now, most of the time everything works fine and the test passes, but some of the time it fails because my delegate methods were called AFTER the operation queue returned control to my wait method.
Test Case
// Set my flags to 'NO'
- (void)setUp {
requestDidFinish = NO;
requestDidFail = NO;
}
- (void)testCreateList {
NSString *testList = #"{\"title\": \"This is a list\"}";
JKEngine *engine = [[JKEngine alloc] initWithDelegate:self];
NSString *requestIdentifier = [engine createList:jsonString];
[self waitUntilEngineDone:engine];
NSString *responseString = responseString_;
[engine release];
GHAssertNotNil(requestIdentifier, nil);
GHAssertTrue(requestDidFinish, nil);
GHAssertTrue([responseString hasPrefix:#"{\"CreateOrEditListResult\""], nil);
}
// Puts the test into a holding pattern until the http request is done
- (void)waitUntilEngineDone:(JKEngine *)engine {
[engine waitUntilFinishedRunning];
}
// The delegate method called on successful completion
- (void)requestFinished:(NSString *)requestIdentifier withResponse:(NSString *)response {
NSLog(#"request did finish");
requestDidFinish = YES;
responseIdentifier_ = [requestIdentifier retain];
responseString_ = [response retain];
}
Engine Code
- (NSString *)createList:(NSString *)list {
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request addRequestHeader:#"Content-Type" value:kContentType];
[request setRequestMethod:kPOST];
request.delegate = self;
[request appendPostData:[list dataUsingEncoding:NSUTF8StringEncoding]];
NSString *requestIdentifier = [NSString stringWithNewUUID];
[operationQueue_ addOperation:request];
[operationDictionary_ setObject:request forKey:requestIdentifier];
return requestIdentifier;
}
// This is the ASIHTTPRequest delegate method that's called on success
// but it sometimes isn't called until AFTER the operationQueue finishes running
- (void)requestFinished:(ASIHTTPRequest *)request {
DLog([request responseString]);
BOOL canNotifiyDelegate = [self.delegate respondsToSelector:#selector(requestFinished:withResponse:)];
if (canNotifiyDelegate) {
NSArray *keyArray = [operationDictionary_ allKeysForObject:request];
NSString *requestIdentifier = [keyArray objectAtIndex:0];
[operationDictionary_ removeObjectForKey:requestIdentifier];
if ([keyArray count] != 1) {
ALog(#"It looks like a request was added to the operation dictionary multiple times. There's a bug somewhere.", nil);
}
[self.delegate requestFinished:requestIdentifier withResponse:[request responseString]];
}
}
- (void)waitUntilFinishedRunning {
[operationQueue_ waitUntilAllOperationsAreFinished];
}
This is the way ASIHTTPRequest works. Delegate methods are called on the main thread, and calls to delegates do not block the request thread, so it's perfectly possible your delegates will be called after the queue finishes.
ASIHTTPRequest calls delegate methods on the main thread, by default GH-Unit runs its tests on a background thread. I'm still a little hazy on exactly what was going on, but forcing my network tests to run on the main thread fixed the problem.
I implemented the following method in my network test class.
- (BOOL)shouldRunOnMainThread {
return YES;
}