Zeiga
Zeiga

Reputation: 201

App crashes at [UIWebView webView:didReceiveTitle:forFrame:]

I am implementing a simple in-app browser. In my home view (UITableViewController), I have something like:

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

        WebViewController *webViewController = [[WebViewController alloc] init];
        switch (indexPath.row) {
            case 0:
                webViewController.stringURL = @"http://www.google.com";
                break;
            case 1:
                webViewController.stringURL = @"http://www.bing.com";
                break;
            default:
                webViewController.stringURL = @"http://stackoverflow.com"; 
                break;
        }

        [self.navigationController pushViewController:webViewController animated:YES];
        [webViewController release];
    }

The app crashed after I repetitively navigated back and forth between my home view and webViewControllera few times.

Inside WebViewController class, I have nothing but a [UIWebView *webView] and a [UIActivityIndicatorView *activityIndicator]. Both are with attributes nonatomic, retain. Here is the implementation.

        #import "WebViewController.h"
        @implementation WebViewController
        @synthesize webView, activityIndicator, stringURL;

        - (void)dealloc
        {
            [self.webView release];
            self.webView.delegate = nil;
            [self.activityIndicator release]; 
            [super dealloc];
        }

        -(void)loadView {
            UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
            self.view = contentView;    

            CGRect webFrame = [[UIScreen mainScreen] applicationFrame];
            webFrame.origin.y = 0.0f;
            self.webView = [[UIWebView alloc] initWithFrame:webFrame];
            self.webView.backgroundColor = [UIColor blueColor];
            self.webView.scalesPageToFit = YES;
            self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
            self.webView.delegate = self;
            [self.view addSubview: self.webView];
            [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.stringURL]]];

            self.activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
            self.activityIndicator.frame = CGRectMake(0.0, 0.0, 30.0, 30.0);
            self.activityIndicator.center = self.view.center;
            [self.view addSubview: self.activityIndicator];
        }

        - (void)viewDidLoad
        {   
            [super viewDidLoad];
            [self loadView];
        }

        - (void)webViewDidStartLoad:(UIWebView *)webView
        {
            // starting the load, show the activity indicator in the status bar
            [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
            [activityIndicator startAnimating];
        }

        - (void)webViewDidFinishLoad:(UIWebView *)webView
        {
            // finished loading, hide the activity indicator in the status bar
            [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
            [activityIndicator stopAnimating];
        }

        @end


I just ran my app in Instruments using the Zombies template, which shows -[UIWebView webView:didReceiveTitle:forFrame:] is the Zombie call. But I still can’t figure out what is actually the problem.

(Please download trace if needed)

Any help is greatly appreciated!


[Update]:

  1. As @7KV7 and @David pointed out, there is an obvious bug in my dealloc function. I should call self.webView.delegate=nil; first before I release self.webView. Sorry about that. Unfortunately, after I fix it, the app still crashes in the same way.
  2. If I delete [webViewController release]; from the first code block, the crash actually is gone. But obviously, there will be memory leak.

Upvotes: 3

Views: 1903

Answers (5)

Deepak Danduprolu
Deepak Danduprolu

Reputation: 44633

First of all, remove that call to loadView in viewDidLoad. The framework will the call the method when it doesn't find a view provided in XIB file. Second, your loadView is filled with memory leaks. You are allocating, initializing and retaining an object every time the method is called. So you are taking ownership twice and releasing it only once in the dealloc.

The objects are not being properly deallocated. You should do something like alloc-init-autorelease to solve this. Next thing is the that every time the controller gets loaded, because of your call to loadView, you end up creating two web view objects and two requests. You lose reference to one of them as you reassign. Herein, lies the problem mentioned in the title. You aren't able to reset the delegate of a web view object that has your controller as a delegate. Imagine a request being completed soon after you leave. Here the message will go to a zombie object. This is a pretty good example for why you need to nil out your delegates.

Upvotes: 3

Priyanka
Priyanka

Reputation: 152

Instead of pushing webViewController,add its view to self.view . Dont call [self loadView]; in viewDidLoad.

Upvotes: 0

skorulis
skorulis

Reputation: 4381

I think what's happening is that you are going back while the page is still loading so the controller gets deallocated and then the webview finishes loading. Try calling [webView stopLoading] in your viewDidUnload method to make sure this isn't happening.

Upvotes: 1

David Gelhar
David Gelhar

Reputation: 27900

Don't know if it's the cause of your problem, but this is definitely wrong:

[self.webView release];
self.webView.delegate = nil;

You cannot (safely) refer to self.webView after you release it!

Upvotes: 0

visakh7
visakh7

Reputation: 26400

- (void)dealloc
{
self.webView.delegate = nil;
[self.webView release];
[self.activityIndicator release]; 
[super dealloc];
}

Try this dealloc. You were releasing the webview and then setting the delegate as nil. You should first set the delegate as nil and then release it. Hope this solves the issue.

Upvotes: 2

Related Questions