NSOperationQueue-提早完成呼叫 [英] NSOperationQueue - Getting Completion Call Too Early
问题描述
我正在使用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屋!