views:

49

answers:

2

I want to put in a timeout in case it takes too long to find my location, send out the relevant url, and parse the xml. It worked when I used performSelector:withObject:afterDelay in the locationManager (just to test getting the xml), but when I put similar code around my parser it doesn't actually abort the parsing. I am testing this by dropping the delay to 0.01.

My problem is: even with the delay set to 0.01, it still waits for all the parsing to complete first, and only then does it put up the alertView that is coded in the parsingDidTimeout method.

I did try this with a timer, and that wasn't working as well as performSelector: does in the other parts of my code. Either way, it doesn't put up the alertView, and stop the parsing, until after the parsing has finished, no matter how long that takes.

I create a url which requires a radius. First I try a small radius, but if I don't get the data I need, I expand the radius and send the url again and parse again. Here is part of my StartParsing method.

 xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
 XMLParser *parser = [[XMLParser alloc] initXMLParser];
[xmlParser setDelegate:parser];

 if (!hadToExpandRadius){//meaning, only do this the first time I send out the url and parse
 [self performSelector:@selector(parsingDidTimeout:) withObject:nil afterDelay:0.01];
 }
 //Start parsing the XML file.
 BOOL success = [xmlParser parse];

 if(success){
 if((didNotGetTheDataYet) && (radius < 500)){
 hadToExpandRadius = YES;
 radius = radius + 35;
 [self startParsing];//do this same method, with larger radius
 }
 else {
 NSLog(@"No Errors");
 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(parsingDidTimeout:) object:nil];}
 [parser release];
 }

 -(void)parsingDidTimeout{
       [xmlParser abortParsing];
  UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:@"Try Later" message:@"We need a better connection. We can get the data later." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
  [servicesDisabledAlert show];
  [servicesDisabledAlert release];
  [myActivityView stopAnimating];

}

Thank you for your help.

A: 

Calling performSelector:withObject:afterDelay: you ask the run loop to call the selector later. But [xmlParser parse] blocks the run loop, so it doesn't have a chance to call you selector.

abortParsing is designed to be called inside parsers' delegate methods.

The workaround can be to parse in a separate thread.

Yuras
Now I have '[self performSelector:@selector(parsingDidTimeout:) withObject:nil afterDelay:2];' and then '[self performSelectorInBackground:@selector(backgroundParse) withObject:nil]'. Then backgroundParse calls '[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(parsingDidTimeout:) object:nil];' when it is successful. Now my problem is that when the timeout occurs, and it calls parsingDidTimeout, I get an unrecognized selector error. I currently have ' [xmlParser abortParsing];' inside the parsingDidTimeout, but even with that out, I get the error. Ugh. Now what? Thanks!
snorkelt
Seems just a typo. In @selector(parsingDidTimeout:) there is a ':', but the method is declared as nullary.
Yuras
I am calling [xmlParser abortParsing] from my parsingDidTimeout method. How can I call it from within my delegate methods? That is on a different thread, but I need to call parsingDidTimeout from my main thread. It still isn't aborting the parsing, even though the alertView pops up.
snorkelt
You can set up an atomic flag property after timeout and then check it at the beginning of every delegate callback and call abortParsing.
Yuras
A: 

Found it -- just extra ":" in my performSelector:@selector(parsingDidTimeout:)! I thought it was something fancy having to do with the second thread. Just syntax.

Thanks for explaining about the parse blocking the run loop. I was hoping not to need another thread, but your suggestion fixed my problem. Thanks.

snorkelt