public
Last active

Ugly as hell workaround for UIWebView crashing on non-main thread dealloc (inside a UIView)

  • Download Gist
gistfile1.m
Objective-C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- (void)dealloc {
webView_.delegate = nil; // delegate is self here, so first set to nil before call stopLoading.
[webView_ stopLoading];
// UIWebView must be released in the main thread, or we get:
// 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...
// This is less evil than the proposed http://stackoverflow.com/questions/945082/uiwebview-in-multithread-viewcontroller
// Calling removeFromSuperview in a dealloc is ugly and evil, but else UIView has a strong reference to UIWebView and our main-release call would be irrelevant.
if (![NSThread isMainThread]) {
[webView_ performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:YES];
}
 
[webView_ performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:YES];
[super dealloc];
}

@nselvis - if i call stopLoading first, couldn't this maybe lead to calling one of my delegate methods within the dealloc? (if the dealloc is on another thread and then context switches...)?

After @anlumo1 'es suggestion, performSelectorOnMainThread immediately calls release if already on main thread, when waitUntilDone is set to YES. (http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html)

Still unsure about that removeFromSuperview.

Also - how would I solve this in ARC?

On ARC: maybe settings webView_ to nil in main thread will just work? Something like

dispatch_sync(dispatch_get_main_queue(), ^() {
        webView_ = nil;
    });

@insanehunter problem is that the block will hold a strong reference to it, so in the end webView will get deallocated in the thread the block was created. (i tried that at first)

Oh, that's really tricky then.

__block variables are not retained, so probably this code should work?

__block id this = self;
dispatch_sync(dispatch_get_main_queue(), ^() {
      this.webView_ = nil;
  });

Anyway, I should go and read on blocks and GCD.

__block will be retained under ARC - Apple changed this behavior. I could use __weak, but that's more overhead, and I'm not sure what other implications this might would have. (or __unsafe_unretained combined with a CFRetain/CFRelease? feels very hacky...)

Super thanks man.. You saved my a**..

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.