Get Native Tracked views

Hi!
Is there any way to know if object is currently native tracked (sgen)?
I'm cleaning up my view hierarchy and i want to remove only objects which are native tracked by xamarin (custom views, and views with events handlers).

Posts

  • adamkempadamkemp USInsider, Developer Group Leader mod

    What exactly do you mean by "native tracked"? I suspect that if what you were looking for were possible then it would be automatic.

  • Ostap.5753Ostap.5753 USMember

    Native tracked objects means that native objects reference count is incremented by xamarin. And native object will persist in memory, until it gets Disposed on managed side. When using reference count extension only objects derived from NSObjects, and objects with WeakReferences set (Delegates, EventHandlers, etc) are Native Tracked.
    It is explained here :

    My question is - is there property or some way to know which object is Native Tracked?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    No, there is not. It sounds like what you're hoping to do is go over some global list of tracked objects and dispose them all. That would not be safe because some (perhaps even most) of those objects are still in use. It would be like trying to fix a memory leak in an unmanaged language by going through every allocation and freeing it.

    What you need to do is figure out which of your objects is causing the leak and changing the code to avoid the leak. That might mean removing event handlers or implementing Dispose to call Dispose on other objects. Without a more specific example it's hard to give more specific advice.

  • Ostap.5753Ostap.5753 USMember

    Thanks for answers.
    I want to go over view hierarchy of dismissed ViewController, not global list, and remove them from parent views,
    But i don't want to remove for example subviews of UIButton or some specific iOs views. I can achieve this without knowing which objects are native tracked using dozens of if-else, but it is not convenient.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Each class you write (whether it's a view controller or a view) should only worry about releasing objects that it created. So if you create views in your view controller and call View.AddSubview then you might want to either remove that view or call Dispose on it when your view controller is being disposed. You shouldn't need to go through the whole view hierarchy for that.

  • Ostap.5753Ostap.5753 USMember

    Yeah, I've used approach described by you. I just want to automate this process, because of very deep view hierarchy in my app.
    When I dismiss VC, pop from Navigation Controller, or set new Root view controller I go through all subviews and child controllers of dismissed controller and remove them from parent views and controllers. This approach works good for me, all reference cycles are gone.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Your approach isn't guaranteed to work in every case. For instance, if you have a field that references a button and you have an event handler on that button then it may also cause a leak, even if that button is not in the view hierarchy. Calling Dispose on the button would prevent that. That pattern can exist in view controllers or in custom view classes, and so there is no simple thing you can do in just one place to prevent this kind of leak. You have to follow the pattern everywhere.

  • Ostap.5753Ostap.5753 USMember

    In case described by you everything works good, we broke down cycle and objects gets disposed.
    Calling dispose on button which is in view hierarchy will cause problems, because for managed peers dispose broke down connection between native and managed peers. Dispose will be called by finalizer if there is no cycle. It is much more safe then calling dispose manually i think.
    The case when it does not work is when we have reference to object, which is not currently in use. For example when we have reference to some cached custom view in viewcontroller, but view is not currently in view hierarchy. Then I release this view manually.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    I didn't follow all of that. All I can say is that your approach is brittle because you're not following the pattern. If you don't follow the pattern then you may get leaks in the future. It's a risk. It's a much better idea to implement this the correct way, which is by implementing Dispose and calling Dispose on any object that might cause a reference cycle. Like this:

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_someOtherNSObject != null)
            {
                _someOtherNSObject.Dispose();
                _someOtherNSObject = null;
            }
        }
    
        base.Dispose(disposing);
    }
    

    You can call Dispose on all of the objects in View.Subviews (though you shouldn't recurse) as well if it makes it easier.

    Again, the reason you want to do it this way instead of just calling RemoveFromSuperview is that this way allows you to also do the right thing within any custom view classes you have implemented. That is, you may know that your view controller doesn't have strong reference cycles caused by objects that aren't in the view hierarchy, but what if one of your custom view classes does? Your approach won't work in that case, and at that point you would have to hack in something to make it work. Following the pattern everywhere makes the code safer.

  • Ostap.5753Ostap.5753 USMember

    Thanks for commenting.
    The question is where to call dispose? If we have cycle dispose will be not called by finalizer.
    I tried to call dispose manually in completion handler of PopViewController and DismissViewController. It caused crashes for me sometimes. And how would you dispose uitableview cells for example?

    Here https://forums.xamarin.com/discussion/4931/summary-of-current-best-practices-for-event-handlers-and-disposing/p1 @RolfBjarneKvinge commented:
    I wouldn't say "Don't call Dispose ..." - I'd prefer it a bit less strict, like "No need to call Dispose unless you have an expensive resource." It doesn't usually hurt to call Dispose otherwise, it's just unnecessary code.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    But you're not even following that advice. If you subscribe to events in ViewWillAppear and unsubscribe in ViewDidDisappear then you may not need to call Dispose on anything, but you also wouldn't need to remove any views from the view hierarchy.

    There's yet another reason not to do what you're trying to do: it makes reusing a view controller impossible. You should be able to present and dismiss a view controller instance multiple times. However, if you remove all of the subviews after dismissing it then you've basically corrupted that view controller's state and made it unusable. It's just not a good practice.

    Instead, you should either try to implement the view controller such that it doesn't leak (perhaps by following the advice in the post you liked to and subscribing/unsubscribing from events as needed) or you should explicitly define the lifetime of the view controller by calling Dispose when you're done with it and implementing Dispose(bool) to break any strong reference cycles.

Sign In or Register to comment.