UIWebView not freeing all live bytes using ARC

浪尽此生 提交于 2019-12-12 07:50:01

问题


I am currently building a navigation controller app in iOS 5.1 that uses ARC. I often need to display webpages and I have made a web viewer that is just a UIWebView with some custom content around the sides. When the user is finished looking at the page they hit the back button which should release all of the memory associated with the custom web viewer. My problem is that all of the memory does not appear to be released when the back button is hit. I have built a toy app (on github) that is just a couple of buttons each having a first responder that calls a different page.

@implementation ViewController
-(IBAction)googlePressed:(id)sender
{
   CustomWebView *customWebView = [[CustomWebView alloc] initWithStringURL:@"http://www.google.com"];
   [self.navigationController pushViewController:customWebView animated:NO];
 }
-(IBAction)ksbwPressed:(id)sender
{
   CustomWebView *customWebView = [[CustomWebView alloc] initWithStringURL:@"http://www.ksbw.com/news/money/Yahoo-laying-off-2-000-workers-in-latest-purge/-/1850/10207484/-/oeyufvz/-/index.html"];
   [self.navigationController pushViewController:customWebView animated:NO];
}
-(IBAction)feedProxyPressed:(id)sender
{
   CustomWebView *customWebView = [[CustomWebView alloc] initWithStringURL:@"http://feedproxy.google.com/~r/spaceheadlines/~3/kbL0jv9rbsg/15159-dallas-tornadoes-satellite-image.html"];
   [self.navigationController pushViewController:customWebView animated:NO];
}
-(IBAction)cnnPressed:(id)sender
{
   CustomWebView *customWebView = [[CustomWebView alloc] initWithStringURL:@"http://www.cnn.com/2012/04/04/us/california-shooting/index.html?eref=rss_mostpopular"];
   [self.navigationController pushViewController:customWebView animated:NO];
}

The CustomWebView is just a UIWebView linked in IB to UIWebView Property

@implementation CustomWebView

@synthesize webView, link;

- (id)initWithStringURL:(NSString *) stringURL
{
   if (self = [super init]) {
       link = stringURL;
   } 
   return self;
}


- (void)viewDidLoad
{
   [super viewDidLoad];
   NSURL *url = [NSURL URLWithString:link];
   NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
   [webView loadRequest:urlRequest];
}

- (void)viewDidUnload
{
   [super viewDidUnload];
}

My problem is that I set the heap baseline to be the initial ViewController once everything is loaded. I then check the heap after loading a page then returning to the ViewController and get the following heapshots:

Which shows that after each series of clicking on a button and returning to the ViewController the heap continues to grow even though the CustomWebView should all be released.

EDIT:

The sample project described above can be found on github


回答1:


I had the same-ish problem, the i found this little piece of magic

/*

 There are several theories and rumors about UIWebView memory leaks, and how
 to properly handle cleaning a UIWebView instance up before deallocation. This
 method implements several of those recommendations.

 #1: Various developers believe UIWebView may not properly throw away child
 objects & views without forcing the UIWebView to load empty content before
 dealloc.

 Source: http://stackoverflow.com/questions/648396/does-uiwebview-leak-memory

 */        
[webView loadHTMLString:@"" baseURL:nil];

/*

 #2: Others claim that UIWebView's will leak if they are loading content
 during dealloc.

 Source: http://stackoverflow.com/questions/6124020/uiwebview-leaking

 */
[webView stopLoading];

/*

 #3: Apple recommends setting the delegate to nil before deallocation:
 "Important: Before releasing an instance of UIWebView for which you have set
 a delegate, you must first set the UIWebView delegate property to nil before
 disposing of the UIWebView instance. This can be done, for example, in the
 dealloc method where you dispose of the UIWebView."

 Source: UIWebViewDelegate class reference    

 */
[webView setDelegate:nil];


/*

 #4: If you're creating multiple child views for any given view, and you're
 trying to deallocate an old child, that child is pointed to by the parent
 view, and won't actually deallocate until that parent view dissapears. This
 call below ensures that you are not creating many child views that will hang
 around until the parent view is deallocated.
 */

[webView removeFromSuperview];



回答2:


I have been using ARC for a while now and I have found in the past that the web view will hold onto whatever was loaded until its told to load something else. The way I get around this is to use a javascript call in the view will (or Did) dissapear method like so:

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [self.webView loadHTMLString:@"<html></html>" baseURL:nil];
}

This releases the memory for me.




回答3:


UIWebView is a memory pig and doesn't deallocate memory well. I put a -(void) dealloc call in your CustomWebView class and it's called as expected when the Back button is hit.

You can try [[NSURLCache sharedURLCache] removeAllCachedResponses] or use and then free your own copy of NSURLCache, but frankly UIWebView never fully cleans up after itself.




回答4:


I havent used ARC yet, but looking at your custom webview code, I think your webviews that you created are still alive.

if you want to be sure that your webviews are killed properly, make sure that in the viewWill/DidDisappear, call [webview stopLoading], and nil the webview.

(caution: if you are pushing another view, then also the webview will be killd, so make sure to handle those issues well)

Most often, I have had issues with webViews where even after popping the viewController, a call back to some webView delegate method would crash the app. (I guess ARC prevents it from crashing).

Let me know if this helps.




回答5:


Brendan a couple of points:

You have not posted enough code to be sure of what is happening. But why not have the controller own a SINGLE UIWebView and rather than creating another one on each button press simply pass it a string with the relevant URL? That is a better design pattern that will guarantee that you never have more than one around.




回答6:


@interface WebViewViewController : UIViewController<UIWebViewDelegate>

- (void)viewDidLoad
{
[super viewDidLoad];
NSString* urlAddress = @"http://www.google.com";
NSURL *url = [NSURL URLWithString:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
self.webView.delegate = self;
[self.webView loadRequest:requestObj];
}

-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[_webView stopLoading];
[_webView loadHTMLString:@"<html><head></head><body></body></html>" baseURL:nil];
[_webView stopLoading];
[_webView removeFromSuperview];
}

- (void)dealloc
{
[_webView stopLoading];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
[_webView stopLoading];
_webView = nil;
}


来源:https://stackoverflow.com/questions/10018418/uiwebview-not-freeing-all-live-bytes-using-arc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!