NSOperationQueue-提早完成呼叫 [英] NSOperationQueue - Getting Completion Call Too Early

查看:69
本文介绍了NSOperationQueue-提早完成呼叫的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using a NSOperationQueue to queue and call a number of Geocoding location lookups. I want to call a completion method when all asynchronicly running lookups have been finished.

-(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];
            }
        }
    }];
}

输出

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

您可以看到在所有地理编码过程+保存事件的实际结束之前调用了队列结束功能.仅在处理完所有排队的查询后,才显示到达队列末尾".我怎样才能使它处于正确的顺序?

As you can see the End of Queue function is called before the actual end of all geocoding processes + saving events. "End of Queue Reached" should only be displayed at the very end when all queued lookups have been processed. How can I get this into the right order?

推荐答案

这里有几个问题.对于其中一个,geocodeAddressString:是异步的,因此它立即返回并且块操作结束,从而允许下一个立即开始.其次,您不应该一个接一个地多次调用geocodeAddressString:.从Apple的文档中可以找到这种方法:

Several issues are coming up here. For one, geocodeAddressString: is asynchronous, so it is returning immediately and the block operation is ending, allowing the next one to start right away. Second, you should not be making multiple calls to geocodeAddressString: one right after the other. From Apple's docs for this method:

After initiating a forward-geocoding request, do not attempt to 
initiate another forward-or reverse-geocoding request.

第三,您尚未在NSOperationQueue上设置最大并发操作数,因此无论如何可能一次执行多个块.

Third, you haven't set a max number of concurrent operations on your NSOperationQueue, so multiple blocks may be executing at once anyway.

由于所有这些原因,您可能希望使用某些GCD工具来跟踪对geocodeAddressString:的呼叫.您可以使用dispatch_semaphore(以确保一个在另一个开始之前完成)和dispatch_group(以确保它们全部完成时知道)来完成此操作-类似于以下内容.假设您已经声明了以下属性:

For all of these reasons, you might want to use some GCD tools to track your calls to geocodeAddressString:. You could do this with a dispatch_semaphore (to make sure one finishes before the other starts) and a dispatch_group (to make sure you know when all of them have finished) -- something like the following. Let's assume you've declared these properties:

@property (nonatomic, strong) NSOperationQueue * geocodeQueue;
@property (nonatomic, strong) dispatch_group_t geocodeDispatchGroup;
@property (nonatomic, strong) dispatch_semaphore_t geocodingLock;

并像这样初始化它们:

self.geocodeQueue = [[NSOperationQueue alloc] init];
[self.geocodeQueue setMaxConcurrentOperationCount: 1];
self.geocodeDispatchGroup = dispatch_group_create();
self.geocodingLock = dispatch_semaphore_create(1);

您可以像这样进行地理编码循环(我对代码进行了一些更改,以使关键部分更加明显):

You could do your geocoding loop like this (I've altered the code a bit to make the key parts more obvious):

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

这篇关于NSOperationQueue-提早完成呼叫的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆