Ios5 Objective-C块、变量和CLGeocoder和/或CLPlacemark

Ios5 Objective-C块、变量和CLGeocoder和/或CLPlacemark,ios5,objective-c-blocks,Ios5,Objective C Blocks,我是Objective-C新手,我的C/C++技能已经相当生疏了。还有什么更好的时间学习iOS开发(!) 我正在尝试使用iOS中的CLGeocoder类反向地理定位一个位置。我可以在块/回调中成功获取我感兴趣的数据(街道地址),但是当我尝试使用该数据填充变量(块外部)时,数据不在那里。就像块中的对象在MapView对象调用它之前消失一样。我正在使用_块,据我所知,它应该允许变量在块外持久存在,但它似乎不允许 下面是有问题的代码: - (void) foundLocation:(CLLocatio

我是Objective-C新手,我的C/C++技能已经相当生疏了。还有什么更好的时间学习iOS开发(!)

我正在尝试使用iOS中的CLGeocoder类反向地理定位一个位置。我可以在块/回调中成功获取我感兴趣的数据(街道地址),但是当我尝试使用该数据填充变量(块外部)时,数据不在那里。就像块中的对象在MapView对象调用它之前消失一样。我正在使用_块,据我所知,它应该允许变量在块外持久存在,但它似乎不允许

下面是有问题的代码:

- (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];
}

我还没有完全意识到“块”的存在,所以问题很可能就在这里,但我不能确切地指出什么是块。

所以你遇到的是一个时间问题。对
reverseGeocodeLocation:completionHandler:
的调用是异步的。调用本身将在实际反向地理定位发生之前立即返回

因此,当调用返回时,您的方法立即继续,并且您正在创建一个带有尚不存在的placemark的注释

然后,在稍后的某个时间点,,您的反向地理定位完成(请记住,它需要对服务进行网络调用以及所有这些)。然后,用新的传入数据触发块

因此,当你的块实际运行时,你创建的这两个局部块变量早已消失。为什么?因为这些变量
placemark
sPlacemark
都是本地(自动)变量,因此对于此方法来说是本地的。它们在方法中存在,然后在方法完成时消失。同样,这种情况发生在你的区块运行之前。(事实证明,该块稍后将写入每个变量的副本,但这并不重要,因为该方法已在那时完成,并且您已经尝试过早读取它们。)

如果在这个方法的末尾放一条NSLog消息,在块中放另一条,您将看到它们触发的顺序。顺序可能与你的想法相反。方法末尾的一个将首先激发,然后是块内的一个

把这想象成一家餐馆。服务员回到厨房,点了一道菜。现在他不再坐在厨房里等食物煮熟,因为这需要时间。因此,他离开了订单,继续他的工作,直到订单准备就绪。现在想象他离开了订单,然后立即检查柜台。看到食物还没有送到,他会很失望的。而这正是你正在做的,当你在厨师们还没来得及煮菜之前,就立即尝试读取
placemark
变量时

那么答案是什么?您可以创建另一个方法来创建注释并将其放置在地图上。该方法应将placemark作为一个参数,然后您可以从块内调用该方法(这也是在您实际拥有placemark之后)。将此视为服务生在下单后要做的所有工作,如向客户下单

还有其他方法可以做到这一点。但对于这里显示的一个placemark,这是一个简单的处理方法。显然,如果找不到placemark或服务不可用等情况,您还应该添加错误处理。如果查找失败,placemark将为零,您可以检查错误以了解更多信息


希望能有帮助。

我遇到了同样的问题。我最终编写了一个自定义方法,该方法将“completion”块作为参数,这样当地理代码完成时,我就可以在该特定块中访问它

--片段


在我的特定用例中,我必须同时支持iOS4和iOS5,因此需要检查和额外的逻辑。我在我的博客上提供了更多的细节:

非常有用!非常感谢菲罗兹。我特别喜欢你把餐厅比喻成完工处理人。再次感谢!很高兴这有帮助。祝应用程序好运。
- (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);
      }
    });
  });
}
}