Return NSString which is set from inside a block

梦想的初衷 提交于 2019-12-10 11:33:23

问题


I have a method which looks like this:

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

And I call it like this:

NSString *city = [self getCityFromLocation:currentLocation];

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

In NSLog, I get:

city 2: (null)
city 1: London

The problem is obvious - it's returning before it's ran the block. How can i get this to work as intended, where it can return the value produced by the block?


回答1:


Intially you have asssign completion block to reverseGeocodeLocation. But It doesn't call at that time. It will call when reverse Geocode process get complete. But city get return immediately. That's why you get like this.

You can solve by assign it to local property. When completion block get executed . So code should be.

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

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

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

     }];



回答2:


Instead of creating block inside getCityFromLocation, make getCityFromLocation as a block (I mean Callback methods).

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);

   }];

}



回答3:


Asynchronous methods, such as reverseGeocodeLocation: you have used here, are normally that for a very good reason - they take time to complete. In that light you should first consider your design and determine whether you really should be trying to use an asynchronous method in a synchronous manner.

If you do decide you need to do this one solution is to use a semaphore. Before the call to reverseGeocodeLocation: create a semaphore with dispatch_semaphore_create (part of GCD, in section 3 of the manual). Then within your block you use dispatch_semaphore_signal to indicate that the string is ready, and outside your block dispatch_semaphore_wait to block until the string is ready.

Your code modified to do this, typed directly into the answer and not executed:

#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;
}

But seriously, consider carefully whether this is what you should be doing.

HTH.



来源:https://stackoverflow.com/questions/16166316/return-nsstring-which-is-set-from-inside-a-block

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!