NSOperationQueue - 太早完成调用

Ber*_*rnd 0 objective-c nsoperationqueue ios objective-c-blocks

我正在使用NSOperationQueue排队并调用多个地理编码位置查找.我想在所有异步运行的查找完成后调用完成方法.

-(void)geocodeAllItems {

    NSOperationQueue *geoCodeQueue = [[NSOperationQueue alloc]init];
    [geoCodeQueue setName:@"Geocode Queue"];

    for (EventItem *item in [[EventItemStore sharedStore] allItems]) {
        if (item.eventLocationCLLocation){
            NSLog(@"-Location Saved already. Skipping-");
            continue;
        }

        [geoCodeQueue addOperationWithBlock:^{

            NSLog(@"-Geocode Item-");
            CLGeocoder* geocoder = [[CLGeocoder alloc] init];
            [self geocodeItem:item withGeocoder:geocoder];

        }];
    }

    [geoCodeQueue addOperationWithBlock:^{
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            NSLog(@"-End Of Queue Reached!-");
        }];
    }];


}

- (void)geocodeItem:(EventItem *)item withGeocoder:(CLGeocoder *)thisGeocoder{

    NSLog(@"-Called Geocode Item-");
    [thisGeocoder 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",placemarks.count,item);
                }
                NSLog(@"-Found Location. Save it-");
                CLPlacemark* placemark = placemarks[0];
                item.eventLocationCLLocation = placemark.location;
                [[EventItemStore sharedStore] saveItems];
            }
        }
    }];
}
Run Code Online (Sandbox Code Playgroud)

产量

[6880:540b] -Geocode Item-
[6880:110b] -Geocode Item-
[6880:540b] -Called Geocode Item-
[6880:110b] -Called Geocode Item-
[6880:110b] -Geocode Item-
[6880:540b] -Geocode Item-
[6880:110b] -Called Geocode Item-
[6880:540b] -Called Geocode Item-
[6880:110b] -Geocode Item-
[6880:580b] -Geocode Item-
[6880:1603] -Geocode Item-
[6880:110b] -Called Geocode Item-
[6880:1603] -Called Geocode Item-
[6880:580b] -Called Geocode Item-
[6880:907] -End Of Queue Reached!-
[6880:907] -Found Location. Save it-
[6880:907] -Found Location. Save it-
[6880:907] -Found Location. Save it-
[6880:907] -Found Location. Save it-
[6880:907] -Found Location. Save it-
[6880:907] -Found Location. Save it-
[6880:907] -Found Location. Save it-
Run Code Online (Sandbox Code Playgroud)

如您所见,在所有地理编码进程+保存事件的实际结束之前调用End of Queue函数."已达到队列结束"应仅在处理完所有排队查找时显示在最后.我怎样才能把它变成正确的顺序?

Pet*_*r E 7

这里有几个问题.例如,它geocodeAddressString:是异步的,因此它立即返回并且块操作结束,允许下一个立即开始.其次,你不应该geocodeAddressString:一个接一个地多次打电话.来自Apple的这种方法的文档:

After initiating a forward-geocoding request, do not attempt to 
initiate another forward-or reverse-geocoding request.
Run Code Online (Sandbox Code Playgroud)

第三,您没有在NSOperationQueue上设置最大并发操作数,因此无论如何都可以同时执行多个块.

出于所有这些原因,您可能希望使用一些GCD工具来跟踪您的呼叫geocodeAddressString:.您可以使用dispatch_semaphore(以确保在另一个启动之前完成)和dispatch_group(以确保您知道所有这些都已完成时) - 如下所示.我们假设您已经声明了这些属性:

@property (nonatomic, strong) NSOperationQueue * geocodeQueue;
@property (nonatomic, strong) dispatch_group_t geocodeDispatchGroup;
@property (nonatomic, strong) dispatch_semaphore_t geocodingLock;
Run Code Online (Sandbox Code Playgroud)

并像这样初始化它们:

self.geocodeQueue = [[NSOperationQueue alloc] init];
[self.geocodeQueue setMaxConcurrentOperationCount: 1];
self.geocodeDispatchGroup = dispatch_group_create();
self.geocodingLock = dispatch_semaphore_create(1);
Run Code Online (Sandbox Code Playgroud)

你可以像这样做你的地理编码循环(我已经改变了一些代码以使关键部分更加明显):

-(void) geocodeAllItems: (id) sender
{
    for (NSString * addr in @[ @"XXX Address 1 XXX", @"XXX Address 2 XXX", @"XXX Address 3 XXXX"]) {
        dispatch_group_enter(self.geocodeDispatchGroup);
        [self.geocodeQueue addOperationWithBlock:^{
            NSLog(@"-Geocode Item-");
            dispatch_semaphore_wait(self.geocodingLock, DISPATCH_TIME_FOREVER);
            [self geocodeItem: addr withGeocoder: self.geocoder];
        }];
    }
    dispatch_group_notify(self.geocodeDispatchGroup, dispatch_get_main_queue(), ^{
        NSLog(@"- Geocoding done --");
    });
}

- (void)geocodeItem:(NSString *) address withGeocoder:(CLGeocoder *)thisGeocoder{

    NSLog(@"-Called Geocode Item-");
    [thisGeocoder geocodeAddressString: address completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error) {
            NSLog(@"Error: geocoding failed for item %@: %@", address, error);
        } else {
            if (placemarks.count == 0) {
                NSLog(@"Error: geocoding found no placemarks for item %@", address);
            } else {
                if (placemarks.count > 1) {
                    NSLog(@"warning: geocoding found %u placemarks for item %@: using the first",placemarks.count, address);
                }
                NSLog(@"-Found Location. Save it:");
            }
        }
        dispatch_group_leave(self.geocodeDispatchGroup);
        dispatch_semaphore_signal(self.geocodingLock);
    }];
}
Run Code Online (Sandbox Code Playgroud)

  • 如果,如你所说,一个人不应该同时进行并发地理编码请求,那么这不是一个简单明了的解决方案吗?为什么不只是有一个需要地理编码的可变位置数组,并让`geocodeItem`只抓取第一个项目,从数组中删除它,对其进行地理编码,并在其完成块中,只需排队下一个地理编码请求(如果有的话),如果没有,你知道他们都完成了吗? (4认同)