views:

271

answers:

2

I'm basically pushing a UIView from a UITableViewController and all it contains is a UIWebView. However when I remove the UIView to return back to the UITableView the app crashes.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
if (indexPath.row == websiteCell) {
    NSString *urlPath = [NSString stringWithFormat:@"http://%@", exhibitor.website];
    WebViewController *webViewController = [[WebViewController alloc] initWithURLString:urlPath];

    // Pass the selected object to the new view controller.
    [self.parentViewController presentModalViewController:webViewController animated:YES];
    [webViewController release];
}

}

If I comment out the [webViewController release] the app doesn't crash, but I know that this would be a leak.

Below is the code for the Web Browser:

#import "WebViewController.h"

@implementation WebViewController

@synthesize webBrowserView;
@synthesize urlValue;
@synthesize toolBar;
@synthesize spinner;
@synthesize loadUrl;

-(id)initWithURLString:(NSString *)urlString {
if (self = [super init]) {
    urlValue = urlString;
}

return self;
}


#pragma mark WebView Controls
- (void)goBack {
[webBrowserView goBack];
}


- (void)goForward {
[webBrowserView goForward];
}


- (void)reload {
[webBrowserView reload];
}

- (void)closeBrowser {
[self.parentViewController dismissModalViewControllerAnimated:YES];
}

#pragma end

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];

CGRect contentRect = self.view.bounds;
//NSLog(@"%f", contentRect.size.height);
float webViewHeight = contentRect.size.height - 44.0f;  // navBar = 44
float toolBarHeight = contentRect.size.height - webViewHeight;


// navigation bar
UINavigationBar *navBar = [[[UINavigationBar alloc] initWithFrame:CGRectMake(0, 20, contentRect.size.width, 44)] autorelease];
navBar.delegate = self;

UIBarButtonItem *doneButton = [[[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:nil action:@selector(closeBrowser)] autorelease];
UINavigationItem *item = [[[UINavigationItem alloc] initWithTitle:@"CEDIA10"] autorelease];
item.leftBarButtonItem = doneButton;

[navBar pushNavigationItem:item animated:NO];
[self.view addSubview:navBar];

// web browser
webBrowserView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 64, contentRect.size.width, webViewHeight)];
webBrowserView.delegate = self;
webBrowserView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
webBrowserView.scalesPageToFit = YES;
[self.view addSubview:webBrowserView];

// buttons
UIBarButtonItem *backButton = [[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"arrowleft.png"] style:UIBarButtonItemStylePlain target:self action:@selector(goBack)] autorelease];
UIBarButtonItem *fwdButton = [[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"arrowright.png"] style:UIBarButtonItemStylePlain target:self action:@selector(goForward)] autorelease];
UIBarButtonItem *refreshButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reload)] autorelease];
UIBarButtonItem *flexSpace = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease];
UIBarButtonItem *fixSpace = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil] autorelease];
[fixSpace setWidth: 40.0f];

spinner = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
[spinner startAnimating];
UIBarButtonItem *loadingIcon = [[[UIBarButtonItem alloc] initWithCustomView:spinner] autorelease];

NSArray *toolBarButtons = [[NSArray alloc] initWithObjects: fixSpace, backButton, fixSpace, fwdButton, flexSpace, loadingIcon, flexSpace, refreshButton, nil];

// toolbar
toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, webViewHeight, contentRect.size.width, toolBarHeight)];
toolBar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
toolBar.items = toolBarButtons;

[self.view addSubview:toolBar];

// load the request
NSURL *requestString = [NSURL URLWithString:urlValue];
[webBrowserView loadRequest:[NSURLRequest requestWithURL: requestString]];

[toolBarButtons release];
}

- (void)viewWillDisappear
{
if ([webBrowserView isLoading]) {
    [webBrowserView stopLoading];
    webBrowserView.delegate = nil;
}
}

#pragma mark UIWebView

- (void)webViewDidStartLoad:(UIWebView*)webView {
[spinner startAnimating];
}

- (void)webViewDidFinishLoad:(UIWebView*)webView {
[spinner stopAnimating];
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
loadUrl = [[request URL] retain];

if ([[loadUrl scheme] isEqualToString: @"mailto"]) {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"CEDIA10" message:@"Do you want to open Mail and exit AREC10?" delegate:self cancelButtonTitle:@"No" otherButtonTitles:@"Yes",nil];
    [alert show]; 
    [alert release];

    return NO;
}

[loadUrl release];

return YES;
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
[spinner stopAnimating];
if (error.code == -1009) {
    // no internet connection
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"CEDIA10" message:@"You need an active Internet connection." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; 
    [alert show]; 
    [alert release];
}
}

#pragma mark UIAlertView

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) {
    [[UIApplication sharedApplication] openURL:loadUrl];
    [loadUrl release];
}
}


- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
[webBrowserView release];
[urlValue release];
[toolBar release];
[spinner release];
[loadUrl release];

webBrowserView = nil;
webBrowserView.delegate = nil;
urlValue = nil;
toolBar = nil;
spinner = nil;
loadUrl = nil;
}

- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;

}


- (void)dealloc {
[webBrowserView release];
[urlValue release];
[toolBar release];
[spinner release];
[loadUrl release];

webBrowserView.delegate = nil;
urlValue = nil;
toolBar = nil;
spinner = nil;
loadUrl = nil;

[super dealloc];
}


@end

Below this is the crash log that I am getting:

Date/Time:       2010-05-13 11:58:20.023 +1000
OS Version:      iPhone OS 3.1.3 (7E18)
Report Version:  104

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x00000000, 0x00000000
Crashed Thread:  0

Thread 0 Crashed:
0   libSystem.B.dylib               0x00090b2c __kill + 8
1   libSystem.B.dylib               0x00090b1a kill + 4
2   libSystem.B.dylib               0x00090b0e raise + 10
3   libSystem.B.dylib               0x000a7e34 abort + 36
4   libstdc++.6.dylib               0x00066390 __gnu_cxx::__verbose_terminate_handler() + 588
5   libobjc.A.dylib                 0x00008898 _objc_terminate + 160
6   libstdc++.6.dylib               0x00063a84 __cxxabiv1::__terminate(void (*)()) + 76
7   libstdc++.6.dylib               0x00063afc std::terminate() + 16
8   libstdc++.6.dylib               0x00063c24 __cxa_throw + 100
9   libobjc.A.dylib                 0x00006e54 objc_exception_throw + 104
10  CoreFoundation                  0x00095bf6 -[NSObject doesNotRecognizeSelector:] + 106
11  CoreFoundation                  0x0001ab12 ___forwarding___ + 474
12  CoreFoundation                  0x00011838 _CF_forwarding_prep_0 + 40
13  QuartzCore                      0x0000f448 CALayerCopyRenderLayer + 24
14  QuartzCore                      0x0000f048 CA::Context::commit_layer(_CALayer*, unsigned int, unsigned int, void*) + 100
15  QuartzCore                      0x0000ef34 CALayerCommitIfNeeded + 336
16  QuartzCore                      0x0000eedc CALayerCommitIfNeeded + 248
17  QuartzCore                      0x00011ee8 CA::Context::commit_root(void*, void*) + 52
18  QuartzCore                      0x00011e80 x_hash_table_foreach + 64
19  QuartzCore                      0x00011e2c CA::Transaction::foreach_root(void (*)(void*, void*), void*) + 40
20  QuartzCore                      0x0000bb68 CA::Context::commit_transaction(CA::Transaction*) + 1068
21  QuartzCore                      0x0000b46c CA::Transaction::commit() + 276
22  QuartzCore                      0x000135d4 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 84
23  CoreFoundation                  0x0000f82a __CFRunLoopDoObservers + 466
24  CoreFoundation                  0x00057340 CFRunLoopRunSpecific + 1812
25  CoreFoundation                  0x00056c18 CFRunLoopRunInMode + 44
26  GraphicsServices                0x000041c0 GSEventRunModal + 188
27  UIKit                           0x00003c28 -[UIApplication _run] + 552
28  UIKit                           0x00002228 UIApplicationMain + 960
29  CEDIA10                         0x00002e16 main (main.m:14)
30  CEDIA10                         0x00002db8 start + 32

Any ideas on why the app is crashing?

+1  A: 

I'd bet that it's the webView that is still sending events to its delegate (your now dealloc'ed view controller).

Also, have you run the Xcode Build & Analyze to find potential memory leaks / over releases?

Ben Scheirman
Adun
A: 

I just figured out what it was. In the WebViewController's dealloc I had this:

- (void)dealloc {
[webBrowserView release];
[urlValue release];
[toolBar release];
[spinner release];
[loadUrl release];

webBrowserView.delegate = nil;
urlValue = nil;
toolBar = nil;
spinner = nil;
loadUrl = nil;

[super dealloc];

}

So by changing it to this it works fine and there are no leaks:

- (void)dealloc {
webBrowserView.delegate = nil;
[webBrowserView stopLoading];
urlValue = nil;
toolBar = nil;
spinner = nil;
loadUrl = nil;

[webBrowserView release];
[urlValue release];
[toolBar release];
[spinner release];
[loadUrl release];

[super dealloc];
}

So I didn't nil the delegate before the release so I webview must have still be sending calls to a delegate that doesn't exists.

Adun
So, what you're saying is that I was right ;)
Ben Scheirman
Yes you are correct. Though it is somewhat strange in how the order of code like this can cause such an issue.
Adun