(Two edits follow the original body of this question, both of which modify the question pretty radically. Don't get hung up on the first part of this--while useful for contextual purposes, I've all but ruled out the original problem I was asking about.)
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.)
EDIT 1:
This just got weirder. I've actually isolated where it crashes, and it's at the bottom of -(void) dealloc
. If I comment out [super dealloc]
, I don't crash! But I can't seem to "step into" [super dealloc]
to see what's happening in the superclass, it just crashes and stops talking to me.
Here's a stacktrace:
#0 0x3018c380 in _WebTryThreadLock ()
#1 0x3018cac8 in _WebThreadAutoLock ()
#2 0x3256e4f0 in -[UITextView dealloc] ()
#3 0x323d7640 in -[NSObject release] ()
#4 0x324adb34 in -[UIView(Hierarchy) removeFromSuperview] ()
#5 0x3256e4a8 in -[UIScrollView removeFromSuperview] ()
#6 0x3256e3e4 in -[UITextView removeFromSuperview] ()
#7 0x324ffa24 in -[UIView dealloc] ()
#8 0x323d7640 in -[NSObject release] ()
#9 0x3256dd64 in -[UIViewController dealloc] ()
#10 0x0000c236 in -[EventsDetailViewController dealloc] (self=0x1c8360, _cmd=0x3321ff2c) at /Users/danielray/Documents/Xcode Projects/EverWondr svn/source/obj-c/Classes/EventsDetailViewController.m:616
#11 0x323d7640 in -[NSObject release] ()
#12 0x336e0352 in __NSFinalizeThreadData ()
#13 0x33ad8e44 in _pthread_tsd_cleanup ()
#14 0x33ad8948 in _pthread_exit ()
#15 0x33731f02 in +[NSThread exit] ()
#16 0x336dfd2a in __NSThread__main__ ()
#17 0x33ad8788 in _pthread_body ()
#18 0x00000000 in ?? ()
Also, it's now clear that the error happens when the back button is fired while [self getDirectionsFromHere]
is running. The window for that is while we're waiting for the stringWithContentsOfURL
call to return.
EDIT 2:
Okay, so noticing the [UITextView dealloc]
near the top of that stack, I realized I only have one UITextView on this view hierarchy, so I commented out the release of that UITextView object in -(void) dealloc
. Next crash, there was [UIWebView dealloc]
in that spot of the trace. Hm! A change! So I commented out my one webView's release in the dealloc method... and now I don't crash. This isn't a solution, because as far as I can tell I'm just leaking those two objects now, but it sure is interesting. Or at least, I hope it is to somebody, because I'm totally at a loss.