Updating a deallocated UIWebView from a background thread
- by Dan Ray
As you can see from the title, I've programmed myself into a corner and I've got several things working against me...
In a UIViewController subclass that manages a large and complex view. One part of it is a UIWebView that contains output from a web request that I had to build and execute, and manually assemble HTML from. Since it takes a second or two to run, I dropped it into the background by calling self performSelectorInBackground:. Then from that method I call there, I use self performSelectorOnMainThread: to get back to the surface of the thread stack to update the UIWebView with what I just got.
Like this (which I've cut down to show only the relevant issues):
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
//then get mapquest directions
NSLog(@"Got called to handle new location!");
[manager stopUpdatingLocation];
[self performSelectorInBackground:@selector(getDirectionsFromHere:) withObject:newLocation];
}
- (void)getDirectionsFromHere:(CLLocation *)newLocation
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CLLocationCoordinate2D here = newLocation.coordinate;
// assemble a call to the MapQuest directions API in NSString *dirURL
// ...cut for brevity
NSLog(@"Query is %@", dirURL);
NSString *response = [NSString stringWithContentsOfURL:[NSURL URLWithString:dirURL] encoding:NSUTF8StringEncoding error:NULL];
NSMutableString *directionsOutput = [[NSMutableString alloc] init];
// assemble response into an HTML table in NSString *directionsOutput
// ...cut for brevity
[self performSelectorOnMainThread:@selector(updateDirectionsWithHtml:) withObject:directionsOutput waitUntilDone:NO];
[directionsOutput release];
[pool drain];
[pool release];
}
- (void)updateDirectionsWithHtml:(NSString *)directionsOutput
{
[self.directionsWebView loadHTMLString:directionsOutput baseURL:nil];
}
This all works totally great, UNLESS I've backed out of this view controller before CLLocationManager hits its delegate method. If this happens after I've already left this view, I get:
2010-06-07 16:38:08.508 EverWondr[180:760b] bool _WebTryThreadLock(bool), 0x1b6830: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
Despite what this says, I can repeatably cause this crash when I back out too early. I'm not at all convinced that attempting a UI update from a background thread is really the issue; I think it's that my UIWebView is deallocated. I suspect that the fact I was just IN a background thread makes the runtime suspect something's up about that, but I feel fairly sure that's not it.
So how do I tell CLLocationManager not to worry about it, when I'm backing out of that view? I tried [self.locationManager stopUpdatingLocation] inside my viewWillDisappear method, but that didn't do it.
(Incidentally, MapQuest's apis are FANTASTIC. Way WAY better than anything Google provides. I can't recommend them highly enough.)