Objective-C: Waiting for the last call in method - ios

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

Related

Dispatch Group not blocking code from running

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

Wait for async blocks completion

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.

Wait for an async methods to finish in a for loop

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

Waiting for nested AFNetworking calls and for loops to finish

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

How to timeout an asynchronous method when ran synchronously

This is essentially what I'm doing to run an asynchronous method synchronously:
This essentially works when called once, but when called multiple times, it will eventually stay inside the while loop and never get signaled. Any ideas on how to set a timer to eventually time out after sometime?
__block SomeClass *result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
dispatch_semaphore_signal(semaphore);
}];
});
// wait with a time limit
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
dispatch_release(semaphore);
Thanks
That looks kind of like GCD abuse to me. ;) Are you running the run loop because this is executing on the main thread? Why not just use a dispatch_async() from your completion handler to invoke a handler on the main thread? eg:
- (void)handleDataReady: (id) results error: (NSError *) error {
// update your app
}
- (void)performAsyncUpdate {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self handleDataReady:responseObject error:error];
}];
});
}
If you really want to make it synchronous, i.e. blocking the calling thread until the operation completes then use the following pattern (of course you want to avoid blocking threads if possible)
NSCondition *waitCondtion = [NSCondition new];
__block BOOL completed = NO;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
[waitCondtion lock];
completed = YES;
[waitCondition signal];
[waitCondition unlock];
}];
});
[waitCondtion lock];
if (!completed)
[waitCondtion wait];
[waitCondition unlock];
You can also use "waitUntilDate:" to timeout the wait after a period.
However, this pattern only works as long as the "someMethodWithCallback does not call its callback block on the same thread that is being blocked. I have copied your code because it is not obvious how "someMethodWithCallback" is implemented. Since this method is using an asynchronous pattern, then it must be doing something asynchronously therefore why are you calling it inside a dispatch_async? What thread will it call its callback block on?
You should "fill" the completion handler with whatever code you require to process the result when the completion handler finished (and also completely removing that run loop).
In order to "abort" an asynchronous operation, you should provide a cancel message which you send the asynchronous result provider.
In your case, since you have a singleton, the cancel message would have to be send like this:
[[SomeManager sharedInstance] cancel];
When the operation receives the cancel message, it should as soon as possible abort its task and call the completion handler with an appropriate NSError object indicating that it has been cancelled.
Note, that cancel messages may be asynchronous - that means, when it returns, the receiver may still execute the task.
You may achieve a "timeout" with setting up a timer, which sends the cancel message the operation, unless it has been invalidated when the operation finished.

Resources