问题描述
我是Objective-C的新手,我的C / C ++技能非常生疏。有什么更好的时间来学习iOS开发(!)
I'm new to Objective-C and my C/C++ skills are quite rusty. What better time to learn iOS development(!)
我正在尝试使用iOS中的CLGeocoder类来反转地理位置。我可以在块/回调中成功获取我感兴趣的数据(街道地址),但是当我尝试使用该数据填充我的变量(块外)时,数据不存在。就好像在MapView对象调用它之前块中的对象消失了。我正在使用__block,根据我的理解,应该允许变量在块之外保留,但似乎没有。
I'm trying to reverse geolocate a position using the CLGeocoder class in iOS. I can successfully get the data I'm interested in (street address) inside the block/callback, however when I try to use that data to populate my variable (outside of the block) the data isn't there. It's as if the object in the block disappears before the MapView object calls it. I'm using __block which as I understand it, should allow the variable to persist outside the block, but it seems not to.
这是有问题的代码:
- (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];
}
我还没有完全绕过'块',所以很可能是问题出在哪里,但我无法确切地指出是什么。
I haven't completely wrapped my head around 'blocks' so it's likely that's where the problem is, but I can't put my finger on exactly what.
帮助?
谢谢事先。
推荐答案
所以你所拥有的是时间问题。对 reverseGeocodeLocation:completionHandler:
的调用是异步。在实际的反向地理定位发生之前,调用本身将立即返回。
So what you have is a timing issue. The call to reverseGeocodeLocation:completionHandler:
is asynchronous. The call itself will return immediately before the actual reverse geolocation happens.
因此,当调用返回时,您的方法会继续,并且您正在创建一个带有尚不存在的地标的注释。
So immediately as the call returns, your method continues, and you're creating an annotation with a placemark that doesn't yet exist.
然后稍后,您的反向地理定位完成(请记住它需要对服务进行网络调用以及所有这些)。然后,使用新的传入数据触发您的块。
Then at some point later, your reverse geolocation is finished (remember it requires a network call to a service and all of that). And then after that, your block is fired with the new incoming data.
因此,当块实际运行时,您创建的这两个本地__block变量早已消失。为什么?因为这些变量 placemark
和 sPlacemark
是本地(自动)变量,这是本方法的本地变量。它们在方法中存在,然后在方法完成时消失。而且,这种情况发生在您的块甚至可以运行之前。 (事实证明这个块会在稍后写入每个变量的副本,但这并不重要,因为这个方法已经完成了,你已经尝试过早地阅读它们了。)
So by the time your block actually runs, these two local __block variables you created are long gone. Why? Because those variables placemark
and sPlacemark
are local (automatic) variables, local to this method. They come into existence within the method, and then go away when the method is finished. And again, this happens before your block even gets to run. (It turns out the block will write into a copy of each variable later, but that doesn't really matter, because this method is finished by then and you've already tried to read them too early.)
如果您在此方法的末尾添加NSLog消息,并在块中添加另一个消息,您将看到它们触发的顺序。订单将与您可能正在思考的相反。方法结尾处的那个将首先触发,然后是块内的那个。
If you put an NSLog message at the end of this method, and another one in your block, you'll see the sequence in which they fire. The order will be the opposite of what you're probably thinking. The one at the end of the method will fire first, and then the one inside the block.
把它想象成一个餐馆。服务员回到厨房并下订单。现在他不会坐在厨房里等待食物煮熟,因为这需要时间。所以他离开了订单并继续他的工作,直到订单准备好。现在想象他离开了订单,然后立即检查柜台。看到那里还没有食物,他会非常失望。当你立即尝试阅读地标
变量时,这正是你正在做的事情,而厨师甚至还有一秒钟来做订单。
Think of this like a restaurant. The waiter goes back to the kitchen and places an order. Now he doesn't sit there at the kitchen and wait for the food to be cooked, because that takes time. So he leaves the order and continues his work until such time as the order is ready. Now imagine he leaves the order, and then immediately checks the counter. He'll be quite disappointed to see that the food isn't there yet. And that's exactly what you're doing when you immediately try to read the placemark
variable before the cooks have had even a second to cook the order.
那么答案是什么?您可以创建另一个创建注释并将其放在地图上的方法。该方法应该将地标作为参数,然后你可以从块中调用该方法(这也是在你真正拥有地标之后。)把这想象成服务员想做的所有工作之后订单已准备就绪,例如将订单发送给客户。
So what's the answer? You could create another method that creates the annotation and places it on the map. That method should take the placemark as a parameter, and then you can call that method from within the block (which again is after you actually have the placemark.) Think of this as all the work the waiter would like to do after the order is ready, like take the order to the customer.
还有其他方法可以做到这一点。但是对于你在这里展示的一个单一地标,这是一种处理这个问题的简单方法。显然,您还应该添加错误处理,以防您找不到地标,或者服务不可用等。如果查找失败,地标将为零,您可以检查错误以获取更多信息。
There are other ways to do this. But for one single placemark as you show here, that's a simple way to handle this. Obviously, you should also add error handling in case you can't find the placemark, or the service is not available, etc. If the lookup fails, the placemark will be nil and you can check the error for more information.
希望有所帮助。
这篇关于Objective-C块,变量和CLGeocoder和/或CLPlacemark的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!