0

I'm having the weirdest issue, and it's doing my head in. A global variable which I've set up within a Singleton is reporting correctly from within the function it's set in, then as NULL from within the very next function (which is where I need to access it), but as correct from another View! So the variable is correctly set, but it's not behaving within a certain function. There is also a weird error warning being generated by the offending line (which I've marked between *).

The warning is:

Property access result unused - getters should not be used for side effects.

Apologies for the very spotty code. I'm prototyping and learning as I go, so it's a mishmash of things I've cobbled from the net. What the code does is recognise a long tap on a mapview, and then places a pin at the location (while recording the location), and I'm trying to use Geocode to show the address at the pin position.

The first function is as follows:

- (void)handleLongPress:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateBegan)
    return;

CGPoint touchPoint = [gestureRecognizer locationInView:self.fullMapView];
CLLocationCoordinate2D touchMapCoordinate =
[self.fullMapView convertPoint:touchPoint toCoordinateFromView:self.fullMapView];

//save new birthplace in global variable        
globalsSingle.gblBirthplace = touchMapCoordinate;

//place user location and record it
MKUserLocation *location = fullMapView.userLocation;
globalsSingle.gblCurrentLocation = location.coordinate;

//first remove any previous birthplace pin
[self removeAllPinsButUserLocation];

[self reverseGeocode];

//place new birthplace pin
MKPointAnnotation *birthPlacePin = [[MKPointAnnotation alloc] init];
birthPlacePin.coordinate = touchMapCoordinate;
birthPlacePin.title = @"My Birthplace";
**** birthPlacePin.subtitle = @"%@", globalsSingle.gblAddress; ****
[self.fullMapView addAnnotation:birthPlacePin];

NSLog(@"gblAddress = %@", globalsSingle.gblAddress);

}

The above function calls the next:

-(void)reverseGeocode {

CLGeocoder *ceo = [[CLGeocoder alloc]init];
CLLocation *loc = [[CLLocation alloc]initWithLatitude:globalsSingle.gblBirthplace.latitude     longitude:globalsSingle.gblBirthplace.longitude]; //insert your coordinates

[ceo reverseGeocodeLocation: loc completionHandler:
 ^(NSArray *placemarks, NSError *error) {
     CLPlacemark *placemark = [placemarks objectAtIndex:0];
     //String to hold address
     NSString *locatedAt = [[placemark.addressDictionary  valueForKey:@"FormattedAddressLines"] componentsJoinedByString:@", "];

     // save the address text
     globalsSingle.gblAddress = locatedAt;

    NSLog(@"addressDictionary %@", placemark.addressDictionary);
    NSLog(@"placemark %@",placemark.region);
    NSLog(@"placemark %@",placemark.country);  // Give Country Name
    NSLog(@"placemark %@",placemark.locality); // Extract the city name
    NSLog(@"location %@",placemark.name);
    NSLog(@"location %@",placemark.ocean);
    NSLog(@"location %@",placemark.postalCode);
    NSLog(@"location %@",placemark.subLocality);
    NSLog(@"location %@",placemark.location);
   //Print the location to console
     NSLog(@"I am currently at %@",locatedAt);
     NSLog(@"gblAddress from reverse Geocode = %@", globalsSingle.gblAddress);

    }
 ];
}

What's even weirder (to me) is that the NSLog's from within reverseGeocode are all printing correctly, but the NSLog from the first function is reporting NULL, and is printing before the one from reverseGeocode even though it's (I assume) being executed second! For example, a debug output is:

2013-05-21 23:41:04.662 Project Name[5659:c07] gblAddress = (null)
2013-05-21 23:41:04.808 Project Name[5659:c07] gblAddress from reverse Geocode = Januária - MG, Brazil

Any help anyone could be bothered to offer I'd appreciate, as I'm bamboozled :)

j0k
  • 22,600
  • 28
  • 79
  • 90

2 Answers2

0

When you are calling [self reverseGeocode]; the rest of handleLongPress will continue to run without waiting for reverseGeocode to finish. This is why you are seeing the print functions being called in an order you weren't expecting.

    [self performSelectorOnMainThread:@selector(reverseGeocode) withObject:nil waitUntilDone:YES];

If handleLongPress is running on the main thread, the above line can replace [self reverseGeocode] and should produce the expected results.

Psiticosis
  • 118
  • 8
  • 2013-05-22 03:24:58.990 Luck Key[6676:c07] -[MapViewController reverseGeocode:]: unrecognized selector sent to instance 0x10d6d290 2013-05-22 03:24:58.991 Luck Key[6676:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MapViewController reverseGeocode:]: unrecognized selector sent to instance 0x10d6d290' *** First throw call stack: () libc++abi.dylib: terminate called throwing an exception It compiled ok though... – Ken Williamson May 21 '13 at 17:27
  • Ahh, my fault. Take the trailing colon off of the selector. I'll edit my answer to reflect this. Since your method does not take an object, this is not needed. – Psiticosis May 21 '13 at 20:11
  • That now runs without crashing, but still it only shows "%@" as the Subtitle text o_O. As I said, I really think this is a limitation of the MKAnnotationPoint.subtitle/title properties. I don't think they can take variables, only straight text. That's my newbie summation :) Many thanks for your help though Psiticosis. I don't have the reputation or whatever to upvote you yet, but I'm amazed at this site and how helpful you guys are! – Ken Williamson May 22 '13 at 03:48
  • P.S. I'm going to use your version of this function call, as it is more correct than mine now that the solution below this has solved it. Many thanks! I'll come back and upvote when I can :) – Ken Williamson May 22 '13 at 05:01
  • Thanks for the feedback, still building my reputation and learning as well. Calling [self method] is just as correct if you're not worried about the code underneath the call running first. I would usually stick with that actually, but since you were having an issue with one returning the NSLog before you expected it to this made sense at the time to test that out. MKPointAnnotation can definitely take variables for the text. If gblAddress is a NSString you should be able to just say birthPlacePin.subtitle = globalsSingle.gblAddress; – Psiticosis May 22 '13 at 20:09
0

The method reverseGeocodeLocation:completionHandler: is executed asynchronously, which means that it will move on to the next lines before it finishes.

Asynchronous vs synchronous execution, what does it really mean?

It is called asynchronously because the method reverseGeocodeLocation:completionHandler: might need some time to do it, and when it is finished, the completion block is called.

You should place the new birthplace pin only after the completion block of the reverseGeocodeLocation is called, for example inside the completion block, to ensure you have got the placemark data first before placing the pin. Or you can just update the subtitle of the newly added pin inside the completion block.

[ceo reverseGeocodeLocation: loc completionHandler:
 ^(NSArray *placemarks, NSError *error) {
     CLPlacemark *placemark = [placemarks objectAtIndex:0];
     //String to hold address
     NSString *locatedAt = [[placemark.addressDictionary  valueForKey:@"FormattedAddressLines"] componentsJoinedByString:@", "];

     // save the address text
     globalsSingle.gblAddress = locatedAt;

     //place new birthplace pin
     MKPointAnnotation *birthPlacePin = [[MKPointAnnotation alloc] init];
     birthPlacePin.coordinate = globalsSingle.gblBirthplace;
     birthPlacePin.title = @"My Birthplace";
     birthPlacePin.subtitle = globalsSingle.gblAddress;
     [self.fullMapView addAnnotation:birthPlacePin];
    }
 ];
}
Community
  • 1
  • 1
Valent Richie
  • 5,226
  • 1
  • 20
  • 21
  • Thankyou for the reply, and that is actually a great idea. However... no dice. I think there is something about MKPointAnnotation subtitle - or any of the other elements - that can't play with a variable. It takes straight text fine. I'm going to try to use a different implementation via MKAnnotation or MKAnnotationView... maybe... – Ken Williamson May 21 '13 at 17:21
  • P.S. The result was the same issue I've been having... the subtitle value refuses to display, and there's still that esoteric warning. – Ken Williamson May 21 '13 at 17:29
  • @KenWilliamson then it is a different issue altogether. I thought the question was about the sequence of the code executed. – Valent Richie May 22 '13 at 00:23
  • Oh yes it was as well, and thankyou for the heads up on that. There were a few things going on that perplexed me, and I think it's come down to this MKPointAnnotation issue (I think, as it can't be anything else). Very odd to write a class that doesn't take variables for the text values if that's the case though... kind of useless actually. – Ken Williamson May 22 '13 at 03:50
  • P.S. I'd be upvoting all you guys if I could - my reputation hasn't hit 15 yet. – Ken Williamson May 22 '13 at 03:51
  • @KenWilliamson: I updated my answer, especially the part `birthPlacePin.subtitle = globalsSingle.gblAddress;`. See if it works. You can accept the answer that is correct by clicking the big tick mark :) – Valent Richie May 22 '13 at 04:39
  • verbumdei, you are a champion. That works! I don't understand quite *why* though - is it to do with the encapsulation of the reverseGeocode function and it's asynchronous nature? That makes calling a variable outside it (i.e. touchMapCoordinate) impossible unless it's global? Either way, many (many) thanks man. – Ken Williamson May 22 '13 at 05:00
  • @KenWilliamson: You're welcome. No, it is related to the reverseGeocode or the asynchronous nature. It is simply that if you want to get a string which contains a string variable, you need to use something like `[NSString stringWithFormat:@"Custom %@", variable]`. The `%@` will be replaced by the variable. – Valent Richie May 22 '13 at 05:03
  • P.S. That will teach me for casting aspersions on API code. I recant in sackcloth and ashes. – Ken Williamson May 22 '13 at 05:08
  • You know though, I thought I tried that! More than once actually... so I have no idea why it works now. Of course, it was a long day yesterday and I probably did something wrong at the time. – Ken Williamson May 22 '13 at 05:10