Reputation: 201
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 webViewController
a 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]:
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. [webViewController release];
from the first code block, the crash actually is gone. But obviously, there will be memory leak.Upvotes: 3
Views: 1903
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
Reputation: 152
Instead of pushing webViewController,add its view to self.view . Dont call [self loadView]; in viewDidLoad.
Upvotes: 0
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
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
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