我是Objective-C的新手,我的C / C ++技能非常生疏。有什么更好的时间学习iOS开发(!)
我正在尝试使用iOS中的CLGeocoder类来反转地理位置。我可以在块/回调中成功获取我感兴趣的数据(街道地址),但是当我尝试使用该数据填充我的变量(块外)时,数据不存在。就好像在MapView对象调用它之前块中的对象消失了。我正在使用__block,根据我的理解,应该允许变量在块之外保留,但似乎没有。
以下是相关代码:
- (void) foundLocation:(CLLocation *)loc
{
CLLocationCoordinate2D coord = [loc coordinate];
// Get our city and state from a reversegeocode lookup and put them in the subtitle field (nowString).
// reversegeocode puts that information in a CLPlacemark object
// First, create the CLGeocoder object that will get us the info
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
// Next create a CLPlacemark object that we can store what reverseGeocodeLocation will give us containing the location data
__block CLPlacemark *placemark = [[CLPlacemark alloc]init];
__block NSString *sPlacemark = [[NSString alloc]init];
// This next bit is where things go awry
[geocoder reverseGeocodeLocation:loc completionHandler:
^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0)
{
placemark = [placemarks objectAtIndex:0];// this works!!
sPlacemark = [placemark thoroughfare]; // as does this! I can see the street address in the variable in the debugger.
}
}];
MapPoint *mp = [[MapPoint alloc] initWithCoordinate:coord
title:[locationTitleField text]
subtitle:sPlacemark];
// add it to the map view
[worldView addAnnotation:mp];
// MKMapView retains its annotations, we can release
[mp release];
// zoom region to this location
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 250, 250);
[worldView setRegion:region
animated:YES];
[locationTitleField setText:@""];
[activityIndicator stopAnimating];
[locationTitleField setHidden:NO];
[locationManager stopUpdatingLocation];
}
我还没有把头完全包裹在'块'周围,所以很可能就是问题出在哪里,但是我无法准确地指出是什么。
帮助?
提前致谢。
答案 0 :(得分:7)
所以你所拥有的是时间问题。对reverseGeocodeLocation:completionHandler:
的调用是异步。在实际的反向地理定位发生之前,呼叫本身将立即返回。
因此,当调用返回时,您的方法会继续,并且您正在创建一个带有尚不存在的地标的注释。
然后稍后,您的反向地理定位已完成(请记住它需要对服务进行网络调用以及所有这些)。之后,使用新的传入数据触发您的块。
因此,当您的块实际运行时,您创建的这两个本地__block变量早已消失。为什么?因为这些变量placemark
和sPlacemark
是本地(自动)变量,所以此方法是本地的。它们在方法中存在,然后在方法完成时消失。而且,这种情况发生在您的块甚至可以运行之前。 (事实证明该块将在稍后写入每个变量的副本,但这并不重要,因为此方法已经完成,并且您已经尝试过早地阅读它们。)
如果在此方法的末尾放置一条NSLog消息,并在块中放入另一条消息,您将看到它们触发的顺序。订单将与您可能正在思考的相反。方法结尾处的那个将首先触发,然后是块内的那个。
把它想象成一家餐馆。服务员回到厨房并下订单。现在他不会坐在厨房里等待食物煮熟,因为这需要时间。所以他离开了订单并继续他的工作,直到订单准备好。现在想象他离开了订单,然后立即检查柜台。看到那里还没有食物,他会非常失望。当你立即尝试阅读placemark
变量并且厨师甚至还有一秒钟来烹饪订单之前,这正是你正在做的事情。
那么答案是什么?您可以创建另一个创建注释并将其放在地图上的方法。该方法应该将地标作为参数,然后你可以从块内调用该方法(这也是在你真正拥有地标之后。)把这想象成服务员想做的所有工作之后订单准备就绪,就像把订单交给客户一样。
还有其他方法可以做到这一点。但是对于你在这里展示的一个单一地标,这是一种处理这个问题的简单方法。显然,您还应该添加错误处理,以防您找不到地标,或者服务不可用等。如果查找失败,地标将为零,您可以检查错误以获取更多信息。
希望有所帮助。
答案 1 :(得分:1)
Firoze我遇到了同样的问题。我最后编写了一个自定义方法,将一个“完成”块作为一个参数,这样当地理编码完成后,我可以在该特定块中访问它。
- 摘录
- (void)fetchForwardGeocodeAddress:(NSString *)address withCompletionHanlder:(ForwardGeoCompletionBlock)completion {
if (NSClassFromString(@"CLGeocoder")) {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLGeocodeCompletionHandler completionHandler = ^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(@"error finding placemarks: %@", [error localizedDescription]);
}
if (placemarks) {
[placemarks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CLPlacemark *placemark = (CLPlacemark *)obj;
NSLog(@"PLACEMARK: %@", placemark);
if ([placemark.country isEqualToString:@"United States"]) {
NSLog(@"********found coords for zip: %f %f", placemark.location.coordinate.latitude,placemark.location.coordinate.longitude);
if (completion) {
completion(placemark.location.coordinate);
}
*stop = YES;
}
}];
}
};
[geocoder geocodeAddressString:address completionHandler:completionHandler];
} else {
/**
* Since the request to grab the geocoded address is using NSString's initWithContentsOfURL
* we are going to make use of GCD and grab that async. I didn't put this in the
* method itself because there could be an instance where you would want to make a
* synchronous call. Better to have flexibility.
*
* Possible improvement could be to write another method that does the async
* loading for you. However, if you did it that way how would you be notified
* when the results returned. Possibly KVO?
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
CLLocation *location = [address newGeocodeAddress];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(location.coordinate);
}
});
});
}
}
在我的特定用例中,我必须支持iOS4和iOS5,因此检查和额外的逻辑。我在博客上详细介绍了一下:http://blog.corywiles.com/forward-geocoding-in-ios4-and-ios5