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函数."已达到队列结束"应仅在处理完所有排队查找时显示在最后.我怎样才能把它变成正确的顺序?
这里有几个问题.例如,它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)