UIWebView not freeing all live bytes using ARC

2019-03-25 18:09发布

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:

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

6条回答
何必那么认真
2楼-- · 2019-03-25 18:18

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.

查看更多
Juvenile、少年°
3楼-- · 2019-03-25 18:22

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];
查看更多
SAY GOODBYE
4楼-- · 2019-03-25 18:26

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.

查看更多
混吃等死
5楼-- · 2019-03-25 18:29

@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;
}
查看更多
我想做一个坏孩纸
6楼-- · 2019-03-25 18:32

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.

查看更多
狗以群分
7楼-- · 2019-03-25 18:37

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.

查看更多
登录 后发表回答