问题
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