2

我正在使用 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,但我不太确定。我感谢任何帮助以找到正确的方法。

4

2 回答 2

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

最后,我们需要释放组以平衡由返回的 +1 保留计数dispatch_group_create

        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

于 2012-12-21T04:15:52.430 回答
1

看看 NSBlockOperation 与 NSOperationQueue 一起使用

创建一个 NSBlockOperation 然后将所有单独的任务添加为执行块 addExecutionBlock:。设置你的完成块setCompletionBlock:它在 NSOperation 超类中,当所有任务完成时它会被调用。默认情况下,它们不会在主线程上运行,因此如果您希望完成块在主线程上执行,则必须明确告诉它这样做。将 NSBlockOperation 添加到NSOperationQueue addOperation:(NSOperation *) 操作

参考操作队列的并发指南页面

推荐:WWDC 2012 视频会议 225(跳至 16:55)

于 2012-12-21T17:45:17.397 回答