2

Using a UIWebView in my application and I cant seem to release the memory it's using. I have a UIButton which loads a view controller which holds the UIWebView. When the webView is loaded the Real Memory (checking it using Instruments) rises but doesn't get lower after my attempts to release it.

When the button is pressed:

if (childBroswer != null)

        {
            childBroswer.Dispose();
            childBroswer = null;
        }

        childBroswer = new ChildBrowser(url);

        AppDelegate d = (AppDelegate)UIApplication.SharedApplication.Delegate;

        if ( d.rootNavigationController.RespondsToSelector(
            new MonoTouch.ObjCRuntime.Selector("presentViewController:animated:completion:")))
        {
                d.rootNavigationController.PresentViewController(childBroswer, true, null);
        }
        else
        {
            d.rootNavigationController.PresentModalViewController(childBroswer, true);
        }

Child browser class:

 public partial class ChildBrowser : UIViewController
{
    public string _url {get;set;}
    UIActivityIndicatorView activityIndicator;
    UIWebView webBrowser;
    NSUrl nsURL;
    NSUrlRequest nsURLRequest;
    public ChildBrowser (string url) : base ("ChildBrowser", null)
    {
        nsURL = new NSUrl(url);
        nsURLRequest = new NSUrlRequest(nsURL);
    }

    public override void DidReceiveMemoryWarning ()
    {
        // Releases the view if it doesn't have a superview.
        base.DidReceiveMemoryWarning ();

        // Release any cached data, images, etc that aren't in use.
    }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();

        if (webBrowser == null)
        {
            webBrowser = new UIWebView(new RectangleF(0,41,320,380));
        }

        this.Add(webBrowser);

        webBrowser.LoadFinished += (object sender, EventArgs e) => {
            activityIndicator.StopAnimating();
        } ;

        webBrowser.LoadStarted += (object sender, EventArgs e) => {

            if (activityIndicator == null)
            {
                activityIndicator = new  UIActivityIndicatorView(new RectangleF(UIScreen.MainScreen.Bounds.Width / 2 - 50,
                                                                                UIScreen.MainScreen.Bounds.Height / 2 - 30,100,100));
                activityIndicator.Color = UIColor.Black;
            }

            activityIndicator.StartAnimating();
            this.Add(activityIndicator);
        } ;

        webBrowser.LoadHtmlString("<html><head></head><body></body></html>",null);   //stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML = \"\";"];
        webBrowser.LoadRequest(nsURLRequest);

        closeBrowser.TouchUpInside +=  handleClose;


        // Perform any additional setup after loading the view, typically from a nib.
    }

    private void handleClose(object sender, EventArgs e)
    {
        webBrowser.Delegate = null;
        webBrowser.StopLoading();
        webBrowser.Dispose();

        DismissViewController(true,delegate {
            closeBrowser.TouchUpInside -=  handleClose;
            webBrowser.RemoveFromSuperview();
            this.Dispose();
        } );
    }

    public override void ViewWillAppear (bool animated)
    {
        base.ViewWillAppear (animated);


    }

    public override void ViewDidDisappear(bool animated)
    {
        base.ViewDidDisappear(true);
        //webBrowser.RemoveFromSuperview();
    }

    public void webViewDidFinishLoad(UIWebView webView)
    {
        //string u = "";
    }

    protected override void Dispose(bool disposing) 
    {
        ReleaseDesignerOutlets();
        base.Dispose(disposing);
    }

What am i missing?

Thank you!

SuperFrog
  • 7,631
  • 9
  • 51
  • 81

2 Answers2

3

Memory for native objects is not reclaimed when you call Dispose. Calling Dispose only drops the managed reference - but the ObjC runtime (reference counted) won't free the object until there's no native reference as well. See this Q&A for more details...

This means that code such as:

this.Add(webBrowser);

will create such a native reference and is likely (i.e. if not removed elsewhere) to keep the webBrowser instance alive as long as this is alive. If you keep Adding then you memory usage will keep growing.

Community
  • 1
  • 1
poupou
  • 43,413
  • 6
  • 77
  • 174
  • 1
    Thank you. Doesn't this supposed to release it? DismissViewController(true,delegate { closeBrowser.TouchUpInside -= handleClose; this.Dispose(); }); – SuperFrog Apr 26 '13 at 16:01
  • Can't say without seeing the whole source. `this.Dispose` will (again) only drop the managed reference to `this` so if something else (native) has a reference to `this` then it will stay alive (and kept your `UIWebView` alive too). – poupou Apr 26 '13 at 17:07
  • 1
    I have edited the question to include the child browser class. Hope it helps, as this is causing my app a lot of problems... 10X! – SuperFrog Apr 26 '13 at 17:14
  • It's not about the `ChildBrowser ` class - but if anything keeps a reference to it. You better use Instruments to track how many references exists (and who got those references) to debug this. – poupou Apr 26 '13 at 18:23
  • 1
    Thanks, I am trying this, but it's quite hard understanding it. Is there some kind of documentation you recommend? I think maybe learning Objective-C will be faster ;) – SuperFrog Apr 26 '13 at 20:17
2

I'm the OP - It seems that releasing the memory usage of UIWebView is quite hard, even when doing so in Objective-C.

What I have eventually done, which didn't solve the problem but improved the memory release is adding the following lines when closing the UIWebView:

NSUrlCache.SharedCache.RemoveAllCachedResponses();

        NSUrlCache.SharedCache.DiskCapacity = 0;

        NSUrlCache.SharedCache.MemoryCapacity = 0;

        webView.LoadHtmlString("",null);

        webView.EvaluateJavascript("var body=document.getElementsByTagName('body')[0];body.style.backgroundColor=(body.style.backgroundColor=='')?'white':'';");

        webView.EvaluateJavascript("document.open();document.close()");

        webView.StopLoading();

        webView.Delegate = null;

        webView.RemoveFromSuperview();

        webView.Dispose();
SuperFrog
  • 7,631
  • 9
  • 51
  • 81