Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Frustrating UIWebView Delegate Crash issue

I've created an ARC Application that run's perfect. It's got a UINavigationController that I use to push through the views and everything runs fine.

I'm converting the Application to iPad and i've decided to show one of the views as a popover. (I don't like UIPopoverController so i've created my own basic popup). It's added to the view as follows..

MyViewController *hotelinf = [[MyViewController alloc]
initWithNibName:@"MyViewController_iPad" bundle:nil];

[self.view addSubview:hotelinf.view];

The view is added as a subview fine. The view i'm adding contains a UIWebView that has the usual delegate methods in it, but when the view tries to access one of the delegates it simply crashes.

*** -[MyViewController respondsToSelector:]: message sent to deallocated instance 0x7917b90

Specifically, it crashes on this line..

[self.webView loadHTMLString:stringResponse baseURL:nil];

I've displayed views (and UINavigationControllers) as subViews many of times without any issues, although none of them included a UIWebView delegate. I'm guessing I have to set the delegate of my UIViewController istance but i'm not sure how. It's also worth noting that if I push the view in my existing UINavigationController it calls and loads the HTML fine, which surely means it has nothing to do with the code of the view itself.

Any help would be greatly appreciated! Here is the code (in addition to above that shows the controller)..

.h

@interface MyViewController : UIViewController <UIWebViewDelegate, UIActionSheetDelegate> {

//Unrelated IBOutlets

}


@property (nonatomic, strong) UIWebView *webView;

@end

.m

@implementation MyViewController
@synthesize webView;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.webView = [[UIWebView alloc]initWithFrame:CGRectMake(317,283,393,354)];
    self.webView.delegate = self;
    [self.view addSubview:self.webView];

    [NSThread detachNewThreadSelector:@selector(getHTMLString) toTarget:self withObject:nil];  
}

-(void)getHTMLString {

    @autoreleasepool {
        //Download a valid HTML String
        [self performSelectorOnMainThread:@selector(loadHTML) withObject:nil waitUntilDone:NO];
    }
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    self.webView = nil;
}

-(void)loadHTML {


    self.webView.opaque = NO;
    self.webView.backgroundColor = [UIColor clearColor];
    if ([stringResponse isEqualToString:@""]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Could not connect to XXXXX.com. Please verify you are connected to a working 3G/WIFI Network." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];

    } else {
        //it crashes here only when loaded as a subview - the first access to the delegate
        [self.webView loadHTMLString:stringResponse baseURL:nil];
    }   
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
}


- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {

        [self stopIndicator];
} 

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{

    if (error.code == NSURLErrorCancelled) return; // this is Error -999
    [self stopIndicator];

    // report the error inside the webview
    NSString* errorString = [NSString stringWithFormat:
                             @"<html><center><font size=+10              color='black' face='Helvetica'>An error occurred:<br>%@</font></center></html>",
                             error.localizedDescription];
    [self.webView loadHTMLString:errorString baseURL:nil];

    UIAlertView *alert = [[UIAlertView alloc]
                          initWithTitle:@"Cannot load URL."
                          message:@"You have a connection failure. Please    verify you are connected to a WIFI or 3G enabled Network."
                          delegate:self cancelButtonTitle:@"Ok"     otherButtonTitles:nil];
    [alert show];



}

@end
like image 537
user1168056 Avatar asked Dec 17 '22 04:12

user1168056


1 Answers

The issue has nothing to do with the UIWebView, rather with your controller class. Indeed,

MyViewController *hotelinf = [[MyViewController alloc]
   initWithNibName:@"MyViewController_iPad" bundle:nil];

[self.view addSubview:hotelinf.view];

You are allocating the controller and assigning it to a local variable; then you add the controller's view as subview to your current view. Doing that, that view is retained, but what happens to the controller object itself? Are you releasing it? Or it leaks (since it is assigned to a local variable)?

This possibly explains why when later the respondsToSelector method is called, the controller has already been deallocated...

A way to fix this is creating a new property or an ivar in your main controller class and store MyViewController in there. Don't forget to release it in dealloc.

I would also suggest another thing. In:

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    self.webView = nil;
}

set the webView delegate to nil before releasing the view:

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    self.webView.delegate = nil;
    self.webView = nil;
}

And I would also possibly review the reason why you release the webView in viewDidDisappear. On the other hand you allocate it in viewDidLoad. This asymmetry is dangerous, since whenever the main view disappears (for any reason) the webView will be removed and when the view reappears, it is not there anymore.

like image 167
sergio Avatar answered Dec 28 '22 07:12

sergio