I have this in my code:
- (void)loadPanoramaForLocation:(CLLocation *)location
{
dispatch_group_t group = dispatch_group_create();
...
dispatch_group_enter(group);
[self getImageForPanorama:model level:level face:PLCubeFaceOrientationFront completion:^(PLImage * image) {
if (image) {
[cubicPanorama setTexture:[PLTexture textureWithImage:image] face: PLCubeFaceOrientationFront];
dispatch_group_leave(group);
}
}];
...
dispatch_group_enter(group);
[self getImageForPanorama:model level:level face:PLCubeFaceOrientationBack completion:^(PLImage * image) {
if (image) {
[cubicPanorama setTexture:[PLTexture textureWithImage:image] face: PLCubeFaceOrientationBack];
dispatch_group_leave(group);
}
}];
...
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
dispatch_release(group);
NSObject<PLIPanorama> *panorama = cubicPanorama;
[plView setPanorama:panorama];
});
}
I don't use ARC in this code and I want to know, is it the correct way to use dispatch_release here? Where should I put it? Is it ok to put it in dispatch_group_notify? Thanks
You can release it as the last thing in -loadPanoramaForLocation:. dispatch_group_notify() is documented to retain it until the notification block has run to completion. So, once you've called that, there's no need to keep your strong reference.
Related
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();
}];
}
I am trying to fetch data from Firebase and after that update the table(reloading data).
-(void) findEvents{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
dispatch_group_async(group, queue, ^{
[self fetchData];
});
// Add another task to the group
dispatch_group_async(group, queue, ^{
[self reloadTableData];
});
// Add a handler function for when the entire group completes
// It's possible that this will happen immediately if the other methods have already finished
dispatch_group_notify(group, queue, ^{
[self enjoyAfterwards];
});
}
-(void)fetchData{
[[ref child:#"calendar_event" ] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
for(FIRDataSnapshot* snap in snapshot.children){
[self.allEvents addObject:[[GAEvent alloc] initWithParams: snap]];
}
}];
}
-(void)reloadTableData{
NSLog(#"reloading table data");
}
-(void) enjoyAfterwards{
NSLog(#"enjoying");
}
This is my reference. But I am getting the following output:
reloading table data
enjoying
// data is fetched
In Swift, I can use callbacks to achieve this. How do I do this in Objective-C?
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.
});
});
I have a method in which I run a couple of other methods. These have completion blocks, I only want to return a value at the end of my main method once I have a result from each of my sub methods. Example:
-(NSMutableDictionary *)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
}
[self subMethod2Complete:^(NSMutableArray *results)
{
}
//return...
}
I only want to return my dictionary at the end once the two sub method have completed. How can I do this?
I did have the idea of storing a BOOL for each method, so I know, NO incomplete and YES complete. So when both are YES, I return my dict. But how I can call it on time and not prematurely?
Update
I have tweaked my code to use a completion block, so when I finally receive the data from two other completion blocks from other methods, I run the final one with compiled results. Below you can see my method. You can see my method below, no success thus far, the final completion block is still getting called prematurely.
Important bits for me. getTitles and getThumbnails methods. In the completion block of these I get the data I need. Only when I have both of these, do I want to call my final completion block of this main method. As a result, it will pass on both titles and thumbnails once they have been received.
-(void)getFeedForUserID:(NSString *)channelID delegate:(id<YTHelperDelegate>)delegate complete:(void (^)(NSMutableDictionary * result))completionBlock properties:(NSString *)element, ...
{
va_list args;
va_start(args, element);
NSMutableArray *array = [NSMutableArray new];
for (NSString *arg = element; arg != nil; arg = va_arg(args, NSString *)) [array addObject:arg];
va_end(args);
NSMutableDictionary *resultsDict = [NSMutableDictionary new];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
for (NSString *string in array)
{
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[self getTitlesArrayForChannel:channelID completionHandler:^(NSMutableArray *results) {
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:results forKey:kFeedElementTitle];
});
}];
});
}
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[self getThumbnailsArrayForChannel:channelID completionHandler:^(NSMutableArray *results) {
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:results forKey:kFeedElementThumbnail];
});
}];
});
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
completionBlock(resultsDict);
});
}
You can use GCD and the dispatch groups feature. Here's an article that explains it: http://www.objc.io/issue-2/low-level-concurrency-apis.html#groups
For example in your case, your code might look something like this (shamelessly copied from the article and adapted a bit)...
- (void)asyncMethod {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^(){
NSMutableArray * results = [self subMethod1];
dispatch_group_async(group, dispatch_get_main_queue(), ^(){
self.subMethod1Results = results;
});
});
dispatch_group_async(group, queue, ^(){
NSMutableArray * results = [self subMethod2];
dispatch_group_async(group, dispatch_get_main_queue(), ^(){
self.subMethod2Results = results;
});
});
// This block will run once everything above is done:
dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
// notify the app that both sets of data are ready
[self notifyWorkIsDone];
// and release the dispatch group
dispatch_release(group);
});
}
This requires a little modification to how your class works, because the above method is asynchronous (which is a good thing--it's not going to block your app while all that work is being done). All you need is some sort of handler to call and notify your app that your data is ready and you can update your UI or do whatever additional processing is necessary.
You are looking for GCD's dispatch_group APIs. Here is some sample code from Apple's Concurrency Programming Guide:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
});
// Do some other work while the tasks execute.
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// Release the group when it is no longer needed.
dispatch_release(group);
Comments on updated code:
Are you sure your mistake is not that your second if statement checks kFeedElementTitle a second time instead of kFeedElementThumbnail which I think may be what you intended?
Updated with working example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *kFeedElementTitle = #"some";
NSString *kFeedElementThumbnail = #"strings";
NSArray *array = #[#"some", #"test", #"strings"];
NSMutableDictionary *resultsDict = [NSMutableDictionary new];
NSLog(#"App launched");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (NSString *string in array)
{
if ([string isEqualToString:kFeedElementTitle])
{
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:5]; // simulate network call
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:#"title result" forKey:kFeedElementTitle];
NSLog(#"Received title result");
});
});
}
if ([string isEqualToString:kFeedElementThumbnail]) // Note: this was changed to kFeedElementThumbnail from kFeedElementTitle
{
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:10]; // simulate network call
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[resultsDict setObject:#"thumbnail result" forKey:kFeedElementThumbnail];
NSLog(#"Received thumbnail result");
});
});
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"final dictionary: %#", resultsDict);
});
return YES;
}
Output:
2013-07-16 21:02:46.468 d[947:a0b] App launched
2013-07-16 21:02:51.471 d[947:a0b] Received title result
2013-07-16 21:02:56.471 d[947:a0b] Received thumbnail result
2013-07-16 21:02:56.472 d[947:a0b] final dictionary: {
some = "title result";
strings = "thumbnail result";
}
you do not know when the blocks are going to return so you will not know if you have the data at the time, if i may make a suggestion you call a method with in those blocks that method will check to see if both dictionaries are set and if they are then continue with the process otherwise don't continue
- (void)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
self.result1 = results;
[self method3];
}
[self subMethod2Complete:^(NSMutableArray *results)
{
self.results2 = results;
[self method3];
}
}
- (void)method3 {
if ( self.results1 != nil && self.results2 != nil ) {
[self startProcedure];
} else {
// do nothing
}
}
although all together i would suggest reworking your code to do this differently, simply because you can't guarantee that one of the blocks will be done by the time of the return, let alone both of them
you can also do something like this
-(NSMutableDictionary *)mainMethod
{
[self subMethod1Complete:^(NSMutableArray *results)
{
}
[self subMethod2Complete:^(NSMutableArray *results)
{
}
while(result == nil)
sleep(1);
//return...
}
which again is really bad.... it's just better to re-write the code