Objective-C的块,变量和CLGeocoder和/或CLPlacemark(Objectiv

2019-10-31 13:58发布

我是新来的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];
}

我还没有完全包裹我的头周围的“块”,所以很可能这就是问题的所在,但我不能把我的手指上到底是什么。

Answer 1:

所以,你所拥有的是一个时机的问题。 要将呼叫reverseGeocodeLocation:completionHandler:异步的 。 实际的反向地理定位发生之前调用本身将立即返回。

于是立即为调用返回时,你的方法继续,并且您要创建与尚不存在一个标注释。

然后在某一点后 ,你的反向地理定位完成(还记得它需要一个网络呼叫服务和所有的)。 然后,在这之后,你的块与新传入的数据被解雇。

因此,通过你的模块实际上运行的时间,创建这两个地方__block变量早已不复存在。 为什么? 因为这些变量placemarksPlacemark是本地的(自动)变量,局部于该方法。 他们来到这个方法中存在,然后走开时结束该方法。 再次,这种情况发生在你的块甚至已经开始运行。 (原来的块将写入每个变量后的副本,但其实并不重要,因为这种方法是通过接完,你已经尝试过过早阅读。)

如果你把一个NSLog的消息,在这种方法中,而另一个在块的末尾,你会看到他们解雇序列。 此次订货会是怎样的你可能在想相反。 在该方法结束时,应该先触发,然后将块内的一个。

想到这就像一个餐厅。 服务员又回到了厨房和下订单。 现在他不会在厨房坐在那里等待被煮的菜,因为这需要时间。 于是,他离开的命令,并继续他的工作,直到时间的顺序是准备好了。 现在想象他离开的命令,然后立即检查柜台。 他会很失望地看到,食品现在还没有。 而这正是你在做什么,当你马上尝试读取placemark变量的厨师已经收到哪怕一秒钟做饭的顺序。

所以,答案是什么? 你可以创建一个用于创建注释,并把它在地图上的另一种方法。 这种方法应采取标作为参数,然后你可以调用从块内的方法(这又是你确实有地标后)。这个作为所有服务员想做的顺序是工作准备好了,就像拿订单给用户。

还有其他的方法来做到这一点。 但是,对于一个单一标的为您展示在这里,这是一个简单的方法来处理这个问题。 很明显,你还应该添加错误处理的情况下,你无法找到地标,或者服务不可用,等等。如果查找失败,标会是零,您可以检查错误的详细信息。

希望帮助。



Answer 2:

Firoze我遇到了同样的问题。 我最后写的是拍了“完成”块作为PARAM这样,当地理编码结束我将有机会获得它在特定块的自定义方法。

- 片段

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



文章来源: Objective-C Blocks, Variables and CLGeocoder and/or CLPlacemark