Cruces
Cruces

Reputation: 3129

When should I dispose of objects in Xamarin.IOS native?

I am building an application that uses a shared project to hold its business logic.

In this shared project each controller has an equivalent.

I use the ViewDidLoad method to generate the logic for the controller and attach it to itself like this:

    public override void ViewDidLoad() {
        base.ViewDidLoad();
        _logic = new MyControllerLogic();
        _logic.attach(this);
    }

Each logic has its own instance variables etc that should be disposed when they are not used

So when (for example) I navigate backwards from MyController to MyFirstController or if I replace the application's root view controller , the logic behind the navigated out of/replaced controller has to be disposed.

Where should I do this?

In the android part of the project I did it like this:

    protected override void OnPause() {
        base.OnPause();
        if (IsFinishing) {
            _logic?.detach();
            _logic = null;
        }
    }

Upvotes: 0

Views: 741

Answers (4)

Yauhen Sampir
Yauhen Sampir

Reputation: 2104

  1. First approach is to write your own navigation service and manage view controllers. And when you use GoBack, you can execute Dispose method for popped ViewController.
  2. Second one is use framework like MvvmLight, MvvmCross etc. which provides methods in ViewModel's(your business logic) when should you clean/dispose/unsubscribe and etc.

Code should be like this

public class NavigationService
{
   public void NavigateTo(string storyboardName)
   {
      //your logic to present storyboard
   }

   public void GoBack()
  {
     var poppedController = NavigationController.PopViewController(true);
     poppedController.Dispose(); //or your method where you want preclean data;
  }
}

Upvotes: 0

Cruces
Cruces

Reputation: 3129

For those wandering this is the approach I ended up with (this works for Xamarin but it is adaptable to Swift if one needs to):

I created the following class ,and from now on all my UIViewControllers will extend this class

public abstract class BaseViewController : UIViewController {
    protected virtual void Finished() { }

    protected virtual void backPressed() { } //this is just in case I wish to be notified when the user moves back

    public override void ViewWillDisappear(bool animated) {
        base.ViewWillDisappear(animated);
        if (NavigationController != null && NavigationController.ViewControllers.ToList().FirstOrDefault(c => c == this) == null) {
            backPressed();
            Finished();
        }
    }

    public override void DismissViewController(bool animated, [BlockProxy(typeof(AdAction))] Action completionHandler) {
        base.DismissViewController(animated, completionHandler);
        Finished();
    }


    public void Present(string storyboard, bool replace = false, bool animated = true) {
        UIViewController vctl = UIStoryboard.FromName(storyboard, null).InstantiateInitialViewController();
        if (replace) {
            List<UIViewController> old = new List<UIViewController>();
            if (UIApplication.SharedApplication.KeyWindow.RootViewController is UINavigationController nav) {
                old.AddRange(nav.ViewControllers);
            } else {
                old.Add(UIApplication.SharedApplication.KeyWindow.RootViewController);
            }
            if (animated) {
                UIView.Transition(
                    UIApplication.SharedApplication.KeyWindow
                    , 0.25
                    , UIViewAnimationOptions.TransitionCrossDissolve
                    , () => UIApplication.SharedApplication.KeyWindow.RootViewController = vctl
                    , () => {
                        old.ForEach(o => (old as BaseViewController)?.Finished());
                    });
            } else {
                UIApplication.SharedApplication.KeyWindow.RootViewController = vctl;
                old.ForEach(o => (old as BaseViewController)?.Finished());
            }
        } else {
            this.PresentViewController(vctl, animated, null));
        }
    }

    public void AddController(string storyboard, string controller = null, bool animated = true) {
        UIViewController ctl = getController(storyboard, controller);
        this.NavigationController?.PushViewController(ctl, animated);
    }

    public static void ReplaceController(this UINavigationController me, string storyboard, string controller = null, bool animated = true) {
        UIViewController ctl = getController(storyboard, controller);
        UIViewController[] vcl = this.NavigationController?.ViewControllers;
        if (vcl == null) return;
        if (vcl.Length > 0) {
            UIViewController old = vcl[vcl.Length - 1];
            vcl[vcl.Length - 1] = ctl;
            me.SetViewControllers(vcl, animated);
            (old as BaseViewController)?.Finished();
        } else {
            me.PushViewController(ctl, animated);
        }

    }

    private UIViewController getController(string storyboard, string controller = null) {
        if (string.IsNullOrWhiteSpace(controller)) {
            return UIStoryboard.FromName(storyboard, null).InstantiateInitialViewController();
        }
        return UIStoryboard.FromName(storyboard, null).InstantiateViewController(controller);
    }

}

The only downside is that from now on I have to always use Present to show new Controllers and Add/ReplaceController to show new Controllers inside the current Navigation.

If someone has a better idea please tell me, because I will have to do the above for all types of controllers (UITabBarController etc etc) and that could be difficult to maintain.

Upvotes: 0

miguel.de.icaza
miguel.de.icaza

Reputation: 32694

You should use an Unwind Segue on the view controller, this is the only reliable way of knowing when a view controller has been dismissed:

https://developer.apple.com/library/archive/technotes/tn2298/_index.html

Here is a Xamarin example:

https://github.com/xamarin/recipes/tree/master/Recipes/ios/general/storyboard/unwind_segue

Upvotes: 1

sevensevens
sevensevens

Reputation: 1750

Xamarin is C# based an uses the C# garbage collector. The most you should do is call is GC.Collect();

If you want fine-grained control over memory management, you should use Swift or Objective-C.

Upvotes: 0

Related Questions