Skip to main content

UIWebView - When (or how) does CFData get released?



after multiple days of banging my head against the wall and having sleepless nights I'm hoping to find some help here. I've gone through various posts here, but none of the answers seem to provide a resolution for me.





In short, my problem is that my App crashes after heavy usage (>10min) of the UIWebView (e.g. opening larger news paper sites in series - one after the other).





To give more details: I am writing an iPhone App and from the MainViewController I push a browserViewController on the navigationController. The browserViewController loads a nib which contains a UWebView (I do not create the WebView programatically). The UIWebView is wired up using Interface Builder.





When going back to Main and then going again to the browserViewController, I only recreate the browserViewController if it is nil. (I want to keep the content that is loaded i the UIWebView - only if there is a memory warning shoud this view be unloaded and release all memory used).





In both, MainViewController and browserViewController I am responding to memory warnings, but this seems not to provide enough relief.





Looking at Instruments I noticed that for example CFData(store) keeps increasing. And even if I simulate a memory warning (see code below) and call viewDidUnload on browserViewController, CFData remains allocated and does not get freed.





So my biggest question is:


How to free up memory created from "browsing"?





This counts for two cases:


- how do I make sure that viewDidUnload properly frees memory allocated my CFData(store)?


- how to free up memory when the user keeps loading pages in browserViewController?


.


Who manages CFData?





See below for my simplified sample code:





MainViewController.h







// MainViewController.h

#import "myAppDelegate.h"

#import "BrowserViewController.h"



@interface MainViewController : UIViewController {

BrowserViewController *browViewController;

}



- (void) switchToBrowserViewController;



@end







MainViewController.m







// MainViewController.m

#import "MainViewController.h"



@implementation MainViewController



- (void)didReceiveMemoryWarning {

// Releases the view if it doesn't have a superview.

[super didReceiveMemoryWarning];



[browViewController release];

browViewController = nil;

}



- (void)viewDidUnload {

// Release any retained subviews of the main view.

// e.g. self.myOutlet = nil;

}



- (void)dealloc {

[browViewController release];

browViewController = nil;

[super dealloc];

}



- (void) switchToBrowserViewController {



// create new browViewController if needed

if ( browViewController == nil ) {

browViewController = [[BrowserViewController alloc] initWithNibName:@"BrowserViewController" bundle:nil];

}



browViewController.navigationItem.hidesBackButton = YES;



[((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:YES animated:NO];



[UIView beginAnimations:nil context:NULL];

[UIView setAnimationDuration: 1];

[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:

((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];



[((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController pushViewController:browViewController animated:NO];

[UIView commitAnimations];



}



@end







BrowserViewController.h







// BrowserViewController.h



#import <UIKit/UIKit.h>

#import "myAppDelegate.h"



@interface BrowserViewController : UIViewController <UIWebViewDelegate> {

IBOutlet UITextField *browserURLField;

IBOutlet UIWebView *browserWebView;

}



@property (nonatomic, retain) UIWebView *browserWebView;



- (void) loadURLinBrowser;



@end







BrowserViewController.m







// BrowserViewController.m

#import "BrowserViewController.h"



@implementation BrowserViewController

@synthesize browserWebView;



- (void)viewDidLoad {

[browserWebView setDelegate:self];

[super viewDidLoad];

}



- (void)didReceiveMemoryWarning {

// Releases the view if it doesn't have a superview.

[super didReceiveMemoryWarning];

}



- (void)viewDidUnload {

[super viewDidUnload];



[browserWebView stopLoading];

[browserWebView setDelegate:nil];

[browserWebView removeFromSuperview];

[browserWebView release];

browserWebView = nil;



browserURLField = nil;

}



- (void)dealloc {

[browserURLField release];



browserWebView.delegate = nil;

[browserWebView stopLoading];

browserWebView = nil;

[browserWebView release];



[super dealloc];

}



- (void) switchBackToMainViewController {



[((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:NO animated:NO];



[UIView beginAnimations:nil context:NULL];

[UIView setAnimationDuration: 1];

[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];



[((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController popViewControllerAnimated:NO];



[UIView commitAnimations];

}



- (void) loadURLinBrowser {

NSURL *url = [[NSURL alloc] initWithString:browserURLField.text];

NSMutableURLRequest *request = [[NSURLRequest alloc] initWithURL: url];

[browserWebView loadRequest: request];

[request release];

[url release];

}



@end







I have tried various recommendations from other posts. For example:









  • 1) Loading an empty page into the WebView.







    NSString *html = @"<html><head></head><body></body></html>";

    [browserWebView loadHTMLString:html baseURL:nil];











  • 2) using removeAllCachedResponses on various places in the above code







    [[NSURLCache sharedURLCache] removeAllCachedResponses];











  • 3) setSharedURLCache did also not provide relief ( I also used this in the AppDelegate applicationDidFinishLaunching).







    NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];

    [NSURLCache setSharedURLCache:sharedCache];

    [sharedCache release];











Unfortunately none of this has helped to "clear the cache" or to free memory allocated by CFData(store).





If anyone could shine some light on this and let me know what I'm missing or doing wrong I would greatly appreciate this.





Thanks a lot for your help!


Ralph


.


.





Edit:


After the initial reply from KiwiBastard I added a screen shot that shows what I observe in Instruments:





Instruments





.


.





Edit from June 2010:


I have still not been able to solve this.


In a second attempt, I created the UIWebView completely programmatically.


Still same issue. However I noticed a strange behavior. If I load for example a PDF document into the webView and I do not scroll the PDF page up or down, the webView & data gets successfully released. However as soon as I scroll to the second page, the dealloc won't work any longer and my App ends up running out of memory at some point. This is totally strange and I cannot get this resolved.


Anyone any idea? Help?



Source: Tips4all

Comments

  1. I think what could be happening is that your Browser is never deallocated, and the viewDidUnload is probably never being called.

    Because your MainViewController has a variable of type BrowserViewController that is never released, that will be resident for the life of your app. Also because you are only switching views, the view will stay in memory too.

    Can I suggest you try creating the BrowserViewController variable when you need it, and release it once it has been pushed by the navcontroller eg

    BrowserViewController *browViewController = [[BrowserViewController alloc] initWithNibName:@"BrowserViewController" bundle:nil];

    browViewController.navigationItem.hidesBackButton = YES;

    [((myAppDelegate *)[UIApplication sharedApplication].delegate).navController setNavigationBarHidden:YES animated:NO];

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration: 1];
    [UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:
    ((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController.view cache:YES];

    [((myAppAppDelegate *)[UIApplication sharedApplication].delegate).navController pushViewController:browViewController animated:NO];
    [UIView commitAnimations];
    [browViewController release];


    I know that it will slightly effect performance because it has to load the nib everytime, but you distinctly don't want to cache the vc anyway?

    ReplyDelete
  2. To release CFData you only need to call CFRelease(your CFData object name).

    ReplyDelete
  3. Somewhere I read, this is a well known bug with UIWebView. Some says to use a static webview object to avoid initializing it again and again but couldn't find a proper solution. Even you follows the same approach. Luckily my requirement was a plain web view with an image. So I ended up using a custom controller with a UIImageView and a UITextView without editing. Works fine for me.

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex