I want code within a dispatch group to finish executing before anything else happens, essentially blocking the app from doing anything until this code is done. I can't get the dispatch group to block additional code from running, however. I've tried pretty much every suggestion here on stack, but I don't know what I'm doing want.
My function:
- (void)myFunction {
NSString *myString = #"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(#"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
dispatch_group_leave(group);
NSLog(#"2 we have left the dispatch group");
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"3 notifying that the dispatch group is finished");
}];
NSLog(#"4 all process are complete, we are done");
}
The output I want via log statements = 1,2,3,4
The output I get via log statements = 1,4,2,3
Why is my code skipping over the dispatch group and print 4 before 2 and 3? Any advice as to what I'm doing wrong is appreciated. Thank you!
Update:
Here is my doSomething method. My code keeps hanging on a dismiss call.
doSomething() {
viewController.dismiss(animated: false completion: { [weak self] in
doMoreCode()
})
}
Nothing here actually blocks. dispatch_group_notify just says "when the group finishes, run this." The tool you meant to use was dispatch_group_wait. If you want 1,2,3,4, then you meant this:
- (void)myFunction {
NSString *myString = #"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(#"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
NSLog(#"2 we have left the dispatch group");
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(#"3 notifying that the dispatch group is finished");
NSLog(#"4 all process are complete, we are done");
}
myFunction of course cannot be called on the main queue in iOS (since it blocks and you must never block the main queue). And it also must not be called on the same queue that doSomething:completion: uses for its completion handler (since that queue will be blocked at dispatch_group_wait).
Remember that dispatch_queue_notify just adds a block to a queue to be run sometime in the future. So it's a little unclear how you expect 3 and 4 to work (in my example I just collapsed them, but maybe you're looking for something else).
Another approach is to not block the application, and just schedule stuff to run when it's supposed to. In that case you can use the main queue. It'd look like this:
- (void)myFunction {
NSString *myString = #"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(#"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
NSLog(#"2 we have left the dispatch group");
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"3 notifying that the dispatch group is finished");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"4 all process are complete, we are done");
});
}
Note in both examples, I'm logging 2 before calling dispatch_group_leave. In this example, I'm also registering two things to be run on the main queue (in order) after the group is done. In this case, myFunction will return immediately (so it can be run on the main queue), but everything should print out in order.
Assuming doSomething runs asynchronously, you could wait for the group, but it's generally better to embrace asynchronous patterns, e.g. allow myFunction to return immediately, but supply your own completion handler to that method:
- (void)myFunctionWithCompletion:(void (^)())completion {
NSString *myString = #"Hello world";
dispatch_group_t group = dispatch_group_create();
NSLog(#"1 entering the dispatch group");
dispatch_group_enter(group);
[self doSomething:myString completion:^{
dispatch_group_leave(group);
NSLog(#"2 we have left the dispatch group");
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"3 notifying that the dispatch group is finished");
});
}
And use it like so:
[self myFunctionWithCompletion:^{
NSLog(#"4 all process are complete, we are done");
}];
// note, it's not done when it gets here, though, because it's asynchronous
Clearly, in the above example, the dispatch group is entirely superfluous, but I assume you were doing multiple asynchronous tasks, which is why you introduced the dispatch group. If it really was just this one asynchronous task, you'd just do:
- (void)myFunctionWithCompletion:(void (^)())completion {
NSString *myString = #"Hello world";
[self doSomething:myString completion:^{
NSLog(#"The asynchronous doSomething is done");
completion();
}];
}
Related
I have a method that is being called several times. But I need to act only in the very last method call. I tried dispatch_async but didn't work because still is been queue the calls:
-(void)doingSomething:(NSString*)someValue
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:1.0f];
dispatch_async(dispatch_get_main_queue(), ^{
// do something with the last call
});
});
}
Any of you knows a way to queue the calls and only use the very last call?
I'll really appreciate your help
My suggestion would be to use a dispatch_group. Call dispatch_group_enter before you call dispatch_async, and call dispatch_group_leave and the end of the block that's executed by dispatch_async. Then, after you've enqueued all the blocks, use dispatch_group_notify to schedule the completion block, which will run after all the other dispatch_async blocks have finished.
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
...
dispatch_group_leave(group);
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// This gets called when all the other blocks have finished
});
Alternatively, you could use an NSOperationQueue instead of libdispatch, and make a completion operation which lists every other operation as a dependency. This does have the disadvantage that the completion operation won't be executed on the main queue, though.
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// This gets called when all the other operations have finished
}];
for (...) {
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
...
}];
[completionOperation addDependency:operation];
[operationQueue addOperation:operation];
}
[operationQueue addOperation:completionOperation];
I am creating a serial queue in which i add two task as shown below
dispatch_queue_t serial = dispatch_queue_create("com.apple.serial", DISPATCH_QUEUE_SERIAL);
**//Task 1**
dispatch_async(serial, ^{
[NMUserAPIManager getUserProfileData:^(NMUser *objUser) {
NSLog(#"Get User Profile .....");
_objUser = objUser;
}];
});
**//Task 2**
dispatch_async(serial, ^{
[NMUserAPIManager getUserRecentTransactionData:^(NSDictionary *responseDictionary) {
_accountTableView.hidden = NO;
[self recentTransactionSetup:responseDictionary];
NSLog(#"Get User Recent transaction");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableData];
});
}];
});
Inside that two task i am calling web service with NSURLSession. Problem is that before my Task 1 completion handle Task2 completion handle get called. According to theory by using serial queue each task waits for the previous task to finish before being executed. It my understanding is correct.
NSURLSession's already run on a background thread, so the issue you are seeing here is that as far as your serial queue is concerned once you call 'getUserProfileData:' technically the work for that block in your queue is finished because the NSURLSession is running on a different thread. If your main goal here is to simply call your second task after your first one completes I don't think you need your own queue you would probably be better off simply doing something like:
[NMUserAPIManager getUserProfileData:^(NMUser *objUser) {
NSLog(#"Get User Profile .....");
_objUser = objUser;
[self getUserTransactions];
}];
-(void)getUserTransactions
{
[NMUserAPIManager getUserRecentTransactionData:^(NSDictionary *responseDictionary) {
_accountTableView.hidden = NO;
[self recentTransactionSetup:responseDictionary];
NSLog(#"Get User Recent transaction");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableData];
});
}];
}
EDIT:
If you are looking for something a little more robust I would check out this post for how you can subclass NSOperation to make your own Asynchronous Operation which you can then use with an NSOperationQueue.
For example I have a method with three async blocks. Each block result is needed to perform next block to achieve final methods result. So, what I'm looking for is a nice GCD strategy to make'em perform in a strict order and without dead locks
__block id task1Result;
__block id task2Result;
__block id finalResult;
[self startTask1:^(id result) { task1Result = result }]
[self startTask2:task1Result block:^(id result) { task2Result = result }]
[self startTask3:task2Result block:^(id result) { finalResult = result }]
UPD. I have found a solution:
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
__block id task1Result;
__block id task2Result;
__block id finalResult;
[self startTask1:^(id result) {
task1Result = result;
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
[self startTask2:task1Result block:^(id result) {
task2Result = result;
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
[self startTask3:task2Result block:^(id result) { finalResult = result }];
But in my case I faced a problem with some library method which brings app to deadlock. ><
Create a serial dispatch queue like described here:
https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
In a nutshell:
dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
printf("Do some work here.\n");
});
dispatch_async(queue, ^{
printf("When finished do next task.\n");
});
Be aware that you have to handle the queue yourself.
If each task directly consumes the result of the previous task, can’t you start each one from the completion callback of its predecessor? You’ll still need a dispatch group to wait for the last task to complete, though.
dispatch_group_t group = dispatch_group_create();
__block id result;
dispatch_group_enter(group);
[self startTask1:^(id task1Result) {
[self startTask2:task1Result block:^(id task2Result) {
[self startTask3:task2Result block:^(id finalResult) {
result = finalResult;
dispatch_group_leave(group);
}];
}];
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
One complication you may run into is whether attempting to enqueue tasks from a completion handler runs the risk of deadlock, i.e. if your completion handlers are invoked on the same serial queue that handles enqueueing tasks.
I have a for loop containing three asynchronous methods, and I want to make some treatment after this 3 async methods are finished.
-(void)getAllUsersInformations{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for(User *user in users){
[self getUserInfo:user];
}
//Here, I want to reload the table view for example, after finishing the for loop (executing the whole three methods).
});
}
-(void)getUserInfo:(User*)user{
[self getInformations:user];
[self getExperiences:user];
[self getEducation:user];
}
Do you have any technic to have this result?
Thank you very much.
One GCD approach is to use dispatch_group. So, before you start an asynchronous task, call dispatch_group_enter, and then when the asynchronous task finishes, call dispatch_group_leave, and you can then create a dispatch_group_notify which will be called when the asynchronous tasks finish. You can marry this with a completion block pattern (which is a good idea for asynchronous methods, anyway):
If getInformations, getExperiences and getEducation are, themselves, all asynchronous methods, the first thing you need is some mechanism to know when they're done. A common solution is to implement a completion block pattern for each. For example:
// added completionHandler parameter which will be called when the retrieval
// of the "informations" is done.
- (void)getInformations:(User*)user completionHandler:(void (^)(void))completionHandler {
// do whatever you were before, but in the asynchronous task's completion block, call this
// completionHandler()
//
// for example
NSURLRequest *request;
[NSURLConnection sendAsynchronousRequest:request queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// handle the request here
// the important thing is that the completion handler should
// be called _inside_ the this block
if (completionHandler) {
completionHandler();
}
}];
}
Repeat this process for getExperiences and getEducation, too.
Then, you can use a dispatch group to notify you of when each of these three requests are done done, calling a completion block in getUserInfo when that takes place:
// added completion handler that will be called only when `getInformations`,
// `getExperiences` and `getEducation` are all done.
//
// this takes advantage of the completion block we added to those three
// methods above
- (void)getUserInfo:(User*)user completionHandler:(void (^)(void))completionHandler {
dispatch_group_t group = dispatch_group_create();
// start the three requests
dispatch_group_enter(group);
[self getInformations:user completionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self getExperiences:user completionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self getEducation:user completionHandler:^{
dispatch_group_leave(group);
}];
// this block will be called asynchronously only when the above three are done
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (completionHandler) {
completionHandler();
}
});
}
And you then repeat this process at the getAllUsersInformations:
// call new getUserInfo, using dispatch group to keep track of whether
// all the requests are done
-(void)getAllUsersInformations {
dispatch_group_t group = dispatch_group_create();
for(User *user in users){
dispatch_group_enter(group);
[self getUserInfo:user completionHandler:^{
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
Two final thoughts:
Having outlined all of that, I must confess that I would probably wrap these requests in concurrent/asynchronous custom NSOperation subclasses instead of using dispatch groups. See the "Configuring Operations for Concurrent Execution" section of the Concurrency Programming Guide. This is a more radical refactoring of the code, so I won't tackle that here, but it lets you constrain the number of these requests that will run concurrently, mitigating potential timeout issues.
I don't know how many of these user requests are going on, but you might want to consider updating the UI as user information comes in, rather than waiting for everything to finish. This is, again, a more radical refactoring of the code, but might lead to something that feels more responsive.
Try to do a block with completion, you can't do this with a for loop if the methods are async. you have to call getUserInfo one by one after the completion of the previous. I think this gonna be solved your problem.
-(void)getAllUsersInformations{
[self registerUserAtIndex:0];
}
- (void) registerUserAtIndex: (NSInteger ) userIndex
{
RegisterOperation *op = [[RegisterOperation alloc] initWithUser:[users objectAtIndex:userIndex]];
[RegisterOperation setResultCompletionBlock:^(BOOL *finished, NSInteger userIndex) {
dispatch_async(dispatch_get_main_queue(), ^{
if (userIndex++ < [users count] {
[self registerUserAtIndex:userIndex++];
} else {
[myTableView reloadData];
}
}];
[[NSOperationQueue mainQueue] addOperation:op];
}
Hope this will help you.
Rop Answer with swift:
func processData()
{
let group: dispatch_group_t = dispatch_group_create()
for item in data as! Object {
dispatch_group_enter(group)
item.process(completion: {() -> (Void) in
dispatch_group_leave(group)
})
}
dispatch_group_notify(group, dispatch_get_main_queue(), {
//Do whatever you want
})
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
for(User *user in users){
[self getUserInfo:user];
}
dispatch_async(dispatch_get_main_queue(), ^{
//reload tableview , this is on main thread.
});
});
So, I've been searching all over but didn't find a solution, or at least I couldn't apply it.
I've found this thread here on stackoverflow, but didn't succeed in implementing it in my code.
My issue is, that I need to know when nested AFNetworking calls and For loops are done. I've tried it with GCD groups, but with no luck.
The code looks like this:
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_async(queue, ^{
[[JSON GET method using AFNetworking 2.0] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work with the result
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some more work with parts of the result
[[JSON GET method based on result] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some work
[[JSON GET method based on result] success:^(NSArray *result) {
dispatch_group_async(group, queue, ^{
//do some work
for (NSDictionary *resultPartDictionary in result) {
dispatch_group_async(group, queue, ^{
//do some work
});
}
});
}];
});
}
});
}];
});
}
});
}
});
}
Right now, everything works. I'm handling Core Data inside the blocks, so I needed MOCs for every thread, which works as well.
The only thing I'd like to know is how to know when all these blocks finish.
Thank you!
EDIT
So, I've tried using dispatch_group_enter(group) and dispatch_group_leave(group), but it seems to me, that it's just not possible with this embedded architecture. Because of the For loops, the "leave" notifications are either too many, which causes an exception or not enough and the dispatch_group_notify returns too early.
Any ideas on this?
You are looking for dispatch_group_notify and dispatch_group_enter/dispatch_group_leave.
dispatch_group_notify executes the given block in the given queue, when every block in the group is finished.
dispatch_group_enter increases the current count of executing tasks in the group. Every dispatch_group_enter must be balanced with a call to dispatch_group_leave.
dispatch_group_leave decreases the current count of executing tasks in the group.
So, you should trick dispatch_group_notify with increase the number of the tasks in the group before your network calls start and decrease it when everything finished. To achieve this, call dispatch_group_enter before dispatch_async and call dispatch_group_leave in the last thread. Since you know the element count of the every result array, you can check if the current thread is the last one.
dispatch_group_enter(group); // Increases the number of blocks in the group.
dispatch_async(queue, ^{
// Make your AFNetworking calls.
dispatch_group_async(group, queue, ^{
//do some work.
if (isLastThread)
dispatch_group_leave(group); // Decreases the number of blocks in the group.
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // Calls the given block when all blocks are finished in the group.
// All blocks finished, do whatever you like.
});