返回从块内部设置的NSString

时间:2013-04-23 09:55:01

标签: iphone ios objective-c block cllocation

我有一个看起来像这样的方法:

-(NSString *)getCityFromLocation:(CLLocation *)location {

    __block NSString *city;

    CLGeocoder *geocoder = [[CLGeocoder alloc] init];

    [geocoder reverseGeocodeLocation: location completionHandler:
     ^(NSArray *placemarks, NSError *error) {

         //Get address
         CLPlacemark *placemark = [placemarks objectAtIndex:0];

         city = [placemark.addressDictionary objectForKey:@"City"];

         NSLog(@"city 1: %@", city);

     }];

    return city;
}

我称之为:

NSString *city = [self getCityFromLocation:currentLocation];

NSLog(@"city 2: %@", city);

在NSLog中,我得到:

city 2: (null)
city 1: London

问题很明显 - 它在运行块之前返回。如何让它按预期工作,它可以返回块产生的值?

3 个答案:

答案 0 :(得分:1)

最初你有reverseGeocodeLocation的asssign完成块。但它当时没有打电话。它将在reverse Geocode process get complete时调用。但city立即返回。这就是你这样做的原因。

您可以通过将其分配给本地属性来解决。完成块执行时。所以代码应该是。

[geocoder reverseGeocodeLocation: location completionHandler:
     ^(NSArray *placemarks, NSError *error) {

         //Get address
         CLPlacemark *placemark = [placemarks objectAtIndex:0];

         self.city = [placemark.addressDictionary objectForKey:@"City"];

     }];

答案 1 :(得分:1)

不要在getCityFromLocation内创建块,而是将getCityFromLocation作为块(我的意思是回调方法)。

typedef void (^Callback)(BOOL isSuccess, id object);

-(void)getCityFromLocation:(Callback)iCallback
{
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];

    [geocoder reverseGeocodeLocation: location completionHandler:
        ^(NSArray *placemarks, NSError *error) {

       //Get address
       CLPlacemark *placemark = [placemarks objectAtIndex:0];

       city = [placemark.addressDictionary objectForKey:@"City"];

      NSLog(@"city 1: %@", city);
      iCallback(YES,city);

   }];

}

答案 2 :(得分:1)

异步方法,例如你在这里使用的reverseGeocodeLocation:,通常是出于很好的理由 - 它们需要时间来完成。在这种情况下,您应首先考虑您的设计,并确定您是否真的应该尝试以同步方式使用异步方法。

如果您确定需要这样做,一个解决方案是使用信号量。在致电reverseGeocodeLocation:之前,使用dispatch_semaphore_create创建信号量(GCD的一部分,在手册的第3部分中)。然后在您的块中使用dispatch_semaphore_signal表示字符串已准备就绪,并在块dispatch_semaphore_wait之外阻止,直到字符串准备就绪。

您的代码已修改为执行此操作,直接输入答案但未执行

#include <dispatch/dispatch.h>

-(NSString *)getCityFromLocation:(CLLocation *)location
{

   __block NSString *city;

   dispatch_semaphore_t sema = dispatch_semaphore_create(0);

   CLGeocoder *geocoder = [[CLGeocoder alloc] init];

   [geocoder reverseGeocodeLocation: location completionHandler:
      ^(NSArray *placemarks, NSError *error)
      {
         //Get address
         CLPlacemark *placemark = [placemarks objectAtIndex:0];

         city = [placemark.addressDictionary objectForKey:@"City"];
         dispatch_semaphore_signal(sema); // string is ready
      }
   ];

   dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); // wait for string
   dispatch_release(sema); // if you are using ARC & 10.8 this is NOT needed
   return city;
}

但严重的是,请仔细考虑这是否是您应该做的。

HTH。