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

1 つのアイテムだけをジオコーディングするヘルパー メソッドを次に示します。

- (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

NSOperationQueue で使用される NSBlockOperation を見てください。

NSBlockOperation を作成し、個別のタスクをすべて実行ブロック addExecutionBlock:として追加します。完了ブロックsetCompletionBlock:を設定します。これは NSOperation スーパー クラスにあり、すべてのタスクが終了したときに呼び出されます。デフォルトではメイン スレッドでは実行されないため、完了ブロックをメイン スレッドで実行する場合は、明示的に実行するように指示する必要があります。NSBlockOperation をNSOperationQueue addOperation:(NSOperation *)operationに追加します

オペレーション キューの同時実行ガイド ページへの参照

推奨: WWDC 2012 ビデオ セッション 225 (16:55 にスキップ)

于 2012-12-21T17:45:17.397 に答える