PDA

View Full Version : [SOLVED] Why is my code executing out of order?




chrono1081
Feb 7, 2012, 04:45 AM
Hi guys,

Sorry for the barrage of questions lately.

I have this code:


-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t location:(CLLocation *)l
{
self = [super init];

if(self)
{

coordinate = c;

//Create date
NSString *date = [NSDateFormatter localizedStringFromDate: [NSDate date]
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];

//Create reverseGeocode
if(!geoCoder)
{
geoCoder = [[CLGeocoder alloc] init];
}

[geoCoder reverseGeocodeLocation:l
completionHandler:^(NSArray *placemarks, NSError *error) {

for(CLPlacemark *p in placemarks)
{


[self setLocality:[p locality]];
NSLog(@"Inside: %@", [self locality]); //This executes second
[self setAdministrativeArea:[p administrativeArea]];

}

}];


NSLog(@"Outside: %@", [self locality]); //This executes first!!!

[self setTitle:t];
[self setSubtitle:date];

}

return self;
}

Its a test to try and figure out why [self locality] is being assigned nil on the "Outside" NSLog statement but being assigned a value on the "Inside" NSLog statement.

Here is my program output:


2012-02-07 05:36:22.740 Whereami[2393:11603] Outside: (null)
2012-02-07 05:36:23.000 Whereami[2393:11603] Inside: San Francisco

As you can see for some reason the "Outside" is executing before the "Inside" and it is preventing me from finishing my exercise.

I already looked at the documentation, but it doesn't seem to do anything. There is a property called "BOOL isGeocoding" but when I do not believe it does what I think it does because when I wrap it around the NSLog(@"Outside") line that line never executes, suggesting the geoencoder never finishes encoding its value.

Basically I am trying to make everything in the "reverseGeocodeLocation" block execute BEFORE the "NSLog(@"Outside") line and I am having trouble finding a way to do it. Any suggestions would be greatly appreciated.



robbieduncan
Feb 7, 2012, 04:56 AM
The Inside: log statement is in a block. A block is basically a chunk of code and data that you can pass around as an object. So you are passing that into the reverseGeocodeLocation:completionHandler: method as the completionHandler. Whilst I've not read the documentation it seems clear that this method is asynchronous and that the completion handler will be called at some point in the future when the reverse geocode lookup has completed. So the results are exactly what I would expect to see.

Edit: if you want the stuff after all your reverse lookups are started to run after they all complete I'd suggest moving them to another method that you call from within the block that tracks how many outstanding lookups are running and only does that when that counter reaches zero.

chrono1081
Feb 7, 2012, 05:27 AM
The Inside: log statement is in a block. A block is basically a chunk of code and data that you can pass around as an object. So you are passing that into the reverseGeocodeLocation:completionHandler: method as the completionHandler. Whilst I've not read the documentation it seems clear that this method is asynchronous and that the completion handler will be called at some point in the future when the reverse geocode lookup has completed. So the results are exactly what I would expect to see.

Edit: if you want the stuff after all your reverse lookups are started to run after they all complete I'd suggest moving them to another method that you call from within the block that tracks how many outstanding lookups are running and only does that when that counter reaches zero.

Thanks so much! I suspected it had to do with blocks. I'm still fairly fuzzy on them. The book I am following along with is using a deprecated class (MKReverseGeocoder) so I had to wing it and use the new class (CLGeocoder) using the documentation, which normally would be no problem but this stupid CLGeocoder gave me such problems it was unreal.

Needless to say I switched my code around a bit and got it working. Its not the most elegant solution by any means but it works:


-(id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t location:(CLLocation *)l
{
self = [super init];

if(self)
{
//Set properties
coordinate = c;
[self setTitle:t];

//Create reverseGeocode
if(!geoCoder)
{
geoCoder = [[CLGeocoder alloc] init];
}

[geoCoder reverseGeocodeLocation:l
completionHandler:^(NSArray *placemarks, NSError *error) {

//Create date
NSString *locationStamp = [NSDateFormatter localizedStringFromDate: [NSDate date]
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];

//Loop through the placemarks
for(CLPlacemark *p in placemarks)
{
[self setLocality:[p locality]];
[self setAdministrativeArea:[p administrativeArea]];

}

//Create the location stamp format
locationStamp = [locationStamp stringByAppendingString:@" "];
locationStamp = [locationStamp stringByAppendingString:[self locality]];
locationStamp = [locationStamp stringByAppendingString:@" "];
locationStamp = [locationStamp stringByAppendingString:[self administrativeArea]];

//Assign location stamp
[self setSubtitle:locationStamp];
}];
}

return self;
}

-(void)dealloc
{
[geoCoder release];
[super dealloc];
}
@end


Thanks again for the help :)