我正在使用CoreLocation的地理编码器来获取多个地图项的CLLocation坐标。地理编码器在完成每个项目时调用完成块。
如何创建类似的块功能,当这些包含异步地理编码器调用的全部完成时调用? (我可以使用手动计数器。但必须有一个更优雅的解决方案)
到目前为止,这是我的地理编码功能。它循环遍历一系列位置项,并为每个位置项启动一个新的地理编码过程。
-(void)geoCodeAllItems {
for (EventItem* thisEvent in [[EventItemStore sharedStore] allItems]) {
if (![thisEvent eventLocationCLLocation]){ //Only geocode if the item has no location data yet
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder geocodeAddressString:[thisEvent eventLocationGeoQuery] completionHandler:^(NSArray *placemarks, NSError *error) {
if (error){
NSLog(@"\t Geo Code - Error - Failed to geocode";
return;
}
if (placemarks)
{
if ([placemarks count] > 1) NSLog(@"\t Geo Code - Warning - Multiple Placemarks (%i) returned - Picking the first one",[placemarks count]);
CLPlacemark* placemark = [[CLPlacemark alloc]initWithPlacemark:[placemarks objectAtIndex:0]];
CLLocationCoordinate2D placeCoord = [[placemark location]coordinate];
[thisEvent setEventLocationCLLocation:[[CLLocation alloc]initWithLatitude:placeCoord.latitude longitude:placeCoord.longitude]];
[[EventItemStore sharedStore] saveItems];
} else {
NSLog(@"\t Geo Code - Error - No Placemarks decoded");
}
}];
geocoder = nil;
}
}
}
这基本上可行,但由于异步方式,我不知道所有地理编码活动何时最终结束。
我的感觉是,我要么为此创建一个块,要么使用Grand Central Dispatch,但我不确定。我很感激任何帮助,以找到正确的方法。
答案 0 :(得分:4)
您可以使用GCD调度组执行此操作。另外,我认为您可以使用单个CLGeocoder
发出多个请求。
由于我们可能根本不需要创建组和地理编码器,我们将懒洋洋地创建它们:
-(void)geocodeAllItems {
dispatch_group_t group = NULL;
CLGeocoder *geocoder = nil;
我们遍历这些项目,跳过那些已经过地理编码的项目:
for (EventItem *item in [[EventItemStore sharedStore] allItems]) {
if (item.eventLocationCLLocation)
continue;
现在我们已经找到了需要地理编码的地理编码,如果需要,我们会创建地理编码器和调度组:
if (!geocoder) {
geocoder = [[CLGeocoder alloc] init];
group = dispatch_group_create();
}
我们将使用辅助方法对此项进行地理编码:
[self geocodeItem:item withGeocoder:geocoder dispatchGroup:group];
}
既然我们已经完成了所有项目,我们将检查我们是否对任何项目进行了地理编码:
if (group) {
如果我们对任何地理编码,那么调度组中就会有块。当组变空时,我们会要求组执行通知块:
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"note: all geocoding requests have completed");
});
最后,我们需要释放该组以平衡dispatch_group_create
返回的+1保留计数:
dispatch_release(group);
}
}
以下是仅对一个项目进行地理编码的辅助方法:
- (void)geocodeItem:(EventItem *)item withGeocoder:(CLGeocoder *)geocoder dispatchGroup:(dispatch_group_t)group {
我们“进入”该组。这会原子地增加组的成员资格计数器:
dispatch_group_enter(group);
然后我们就可以开始地理编码了:
[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];
}
}
在地理编码完成块中,完成所有工作后,我们“离开”该组,这会减少其成员数:
dispatch_group_leave(group);
}];
}
当成员资格计数变为零时,该组将执行我们在geocodeAllItems
末尾添加的通知块。
答案 1 :(得分:1)
查看与NSOperationQueue一起使用的NSBlockOperation
创建一个NSBlockOperation,然后将所有单独的任务添加为执行块addExecutionBlock:。设置NSOperation超类中的完成块setCompletionBlock:,并在完成所有任务后调用它。默认情况下,它们不会在主线程上运行,因此如果您希望在主线程上执行完成块,则必须明确告诉它这样做。将NSBlockOperation添加到NSOperationQueue addOperation:(NSOperation *)operation
Reference to the Concurrency Guide page on Operation Queues
推荐:WWDC 2012视频会议225(跳至16:55)