Geocoding Multiple Locations - Knowing When “All” Completion Blocks Have Been Called

梦想的初衷 提交于 2019-12-06 11:29:39

You can use a GCD dispatch group to do this. Also, I think you can make multiple requests with a single CLGeocoder.

Since we might not need to create the group and the geocoder at all, we'll create them lazily:

-(void)geocodeAllItems {
    dispatch_group_t group = NULL;
    CLGeocoder *geocoder = nil;

We loop over the items, skipping the ones that have already been geocoded:

    for (EventItem *item in [[EventItemStore sharedStore] allItems]) {
        if (item.eventLocationCLLocation)
            continue;

Now that we've found one that needs geocoding, we create the geocoder and the dispatch group if we need to:

        if (!geocoder) {
            geocoder = [[CLGeocoder alloc] init];
            group = dispatch_group_create();
        }

We'll use a helper method to geocode just this item:

        [self geocodeItem:item withGeocoder:geocoder dispatchGroup:group];
    }

Now that we've gone through all the items, we'll check whether we geocoded any:

    if (group) {

If we geocoded any, then there are blocks in the dispatch group. We'll ask the group to execute a notification block when it becomes empty:

        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"note: all geocoding requests have completed");
        });

Finally, we need to release the group to balance the +1 retain count returned by dispatch_group_create:

        dispatch_release(group);
    }
}

Here's the helper method that geocodes just one item:

- (void)geocodeItem:(EventItem *)item withGeocoder:(CLGeocoder *)geocoder dispatchGroup:(dispatch_group_t)group {

We “enter” the group. This increments the group's membership counter atomically:

    dispatch_group_enter(group);

Then we can start the geocoding:

    [geocoder geocodeAddressString:item.eventLocationGeoQuery completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error) {
            NSLog(@"error: geocoding failed for item %@: %@", item, error);
        } else {

            if (placemarks.count == 0) {
                NSLog(@"error: geocoding found no placemarks for item %@", item);
            } else {
                if (placemarks.count > 1) {
                    NSLog(@"warning: geocoding found %u placemarks for item %@: using the first", item, placemarks.count);
                }
                CLPlacemark* placemark = placemarks[0];
                thisEvent.eventLocationCLLocation = placemarks[0].location;
                [[EventItemStore sharedStore] saveItems];
            }
        }

In the geocoding completion block, after all the work is done, we “leave” the group, which decrements its membership count:

        dispatch_group_leave(group);
    }];
}

When the membership count goes to zero, the group will execute the notification block we added at the end of geocodeAllItems.

Take a look at NSBlockOperation used with NSOperationQueue

create an NSBlockOperation and then add all of the separate tasks as execution blocks addExecutionBlock: . Set your completion block setCompletionBlock: which is in the NSOperation super class and it will get called when all of the tasks are finished. They will by default not be run on the main thread so if you want your completion block to be executed on the main thread you will have to explicitly tell it to do so. Add the NSBlockOperation to a NSOperationQueue addOperation:(NSOperation *)operation

Reference to the Concurrency Guide page on Operation Queues

Recommend: WWDC 2012 video session 225 (skip to 16:55)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!