NSXMLParser initWithContentsOfURL timeout
How can I set a timeout when I am parsing a feed using initWithContentsOfURL. Sometimes our feeds return a blank response and then it will just try to pa开发者_如何学JAVArse the feed forever. I want to set a timeout of 30 seconds or so that will pop up a UIAlertView and then try and reparse the feed.
NSURL *feedURL = [[NSURL alloc] initWithString:URL];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:feedURL];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
First approach: using a delayed selector
Probably the simplest way to do this is to use NSObject's performSelector:withObject:afterDelay:
method. You can define some method parsingDidTimeout
as such:
- (void)parsingDidTimeout {
if(self.parsingDidComplete == NO) {
[self.parser abortParsing];
// Create your error and display it here
// Try the fetch and parse again...
}
}
This requires that you hang on to the parser as an instance variable (self.parser
), so that you can cancel it from the method you define. It also requires that your parser delegate keep track of whether or not the parser has finished (self.parsingDidComplete
, can be defaulted to NO
and set to YES
in the delegate's parserDidEndDocument:
method). This is to avoid aborting a successful parse. After this is done, all it takes is a simple
[self performSelector:@selector(parsingDidTimeout) withObject:nil afterDelay:30];
and thirty seconds later, your parsing-abort code will get called, and you can do whatever it is you need to do.
Second approach: using a timer
You could make this whole approach (arguably) simpler in the timeout method by using an NSTimer instead of the NSObject method call. That way, if the parser successfully finishes, you can simply invalidate the timer, allowing you to eliminate the if
clause in the parsingDidTimeout
method (and, as a result, also getting rid of the BOOL
ivar). The timer initialization would look like:
NSTimer *timer = [NSTimer timerWithTimeInterval:30.0
target:self
selector:@selector(parsingDidTimeout)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
Doesn't answer your question direction, but you'd have complete control over the request and response cycle (as well as asynchronicity without additional threads) if you used NSURLConnection
to download the data. That's what initWithContentsOfURL:
is doing, under the covers.
Swift 3 example using NSTimer
aka Timer
func startParseTimeoutTimer() {
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { (_) in
if (self.busy) {
self.parser?.abortParsing()
self.parser = nil
print("Show UI as NOT busy; we aborted for timeout \(Thread.current.isMainThread)")
}
}
}
精彩评论