Summary of current best practices for event handlers and disposing

DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭
edited June 2013 in Xamarin.iOS

Recently I've spent some time tracking down why some objects were not getting garbage collected. I've read through various discussions on reasons why and what to do but it feels like there is variance in the recommendations. Also, the samples I've seen on github or the field service app also seem to be inconsistent. And one more factor, I'm a little unclear about how it might have changed post ios6 which seems to have affected the steps required to clean up events and/or dispose items.

Some resources on the topic include...

Discussion threads

Evolve video session on advanced mem management

Docs

Samples

My working conclusion at this point, is that for a normal ViewController:

  • subscribe to events in ViewWillAppear
  • Unsubscribe to events in ViewDidDisappear
  • Dont call dispose unless you have an expensive (big/rare) resource
  • due to the need to unsubscribe, lambdas must either be in a local|instance field, or use a formal method
  • that's it

But it feels like there's more to the story. @Rolf or anyone want to chime in on current best practices?

Dennis

«1

Posts

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    Thanks for the confirmation, Rolf. Definitely understand that the samples aren't always best practices and usually focused on illustrating a single key point. I was just trying to see if there was a more common pattern in them.

    Your last point is right where I'm at, profiling and figuring out what's working and what's not. I will say, the mono profiler is a nice simple tool for finding out what's hanging on to an object yet. THe trick is knowing what to do about it. :-)

    (for the benefit of anyone coming after and reading this, info on how to use the profiler is here at the moment: http://docs.xamarin.com/guides/ios/deployment,_testing,_and_metrics/monotouch_profiler)

    Thanks again.

  • AlexPalmaAlexPalma USMember ✭✭

    Hi Dennis,
    Yes event handlers is one of the most common causes why object will stay in memory when they are no longer needed.
    I started doing what you are saying subscribing to events on the ViewDidAppear and unsubscribing them on the ViewDidDisapear, but that can became a tedious job and if you forget one them your done, I knew there was something better to do than that. bottom line what I wanted was weak event subscribing, so I started goggling it and found this article on stackoverflow. http://stackoverflow.com/questions/1747235/weak-event-handler-model-for-use-with-lambdas
    Based on Benjol answer I created 3 extension methods to work with weak event handlers, bellow is my code with that you can now use lambda expressions with eventhandlers or still use class methods as eventhandlers is up to you.

    /// <summary> /// Static Class that holds the extension methods to handle events using weak references. /// This way we do not need to worry about unregistered the event handler. /// </summary> public static class WeakEventManager { /// <summary> /// This overload handles any type of EventHandler /// </summary> /// <typeparam name="T">The type of the T.</typeparam> /// <typeparam name="TDelegate">The type of the T delegate.</typeparam> /// <typeparam name="TArgs">The type of the T args.</typeparam> /// <param name="subscriber">The subscriber.</param> /// <param name="converter">The converter.</param> /// <param name="add">The add.</param> /// <param name="remove">The remove.</param> /// <param name="action">The action.</param> public static void SetAnyHandler<T, TDelegate, TArgs>(this T subscriber, Func<EventHandler<TArgs>, TDelegate> converter, Action<TDelegate> add, Action<TDelegate> remove, Action<T, TArgs> action) where TArgs : EventArgs where TDelegate : class where T : class { var subsWeakRef = new WeakReference(subscriber); TDelegate handler = null; handler = converter(new EventHandler<TArgs>( (s, e) => { var subsStrongRef = subsWeakRef.Target as T; if (subsStrongRef != null) { action(subsStrongRef, e); } else { remove(handler); handler = null; } })); add(handler); } /// <summary> /// this overload is simplified for generic EventHandlers /// </summary> /// <typeparam name="T">The type of the T.</typeparam> /// <typeparam name="TArgs">The type of the T args.</typeparam> /// <param name="subscriber">The subscriber.</param> /// <param name="add">The add.</param> /// <param name="remove">The remove.</param> /// <param name="action">The action.</param> public static void SetAnyHandler<T, TArgs>(this T subscriber, Action<EventHandler<TArgs>> add, Action<EventHandler<TArgs>> remove, Action<T, TArgs> action) where TArgs : EventArgs where T : class { SetAnyHandler<T, EventHandler<TArgs>, TArgs>(subscriber, h => h, add, remove, action); } /// <summary> /// this overload is simplified for EventHandlers. /// </summary> /// <typeparam name="T">The type of the T.</typeparam> /// <param name="subscriber">The subscriber.</param> /// <param name="add">The add.</param> /// <param name="remove">The remove.</param> /// <param name="action">The action.</param> public static void SetAnyHandler<T>(this T subscriber, Action<EventHandler> add, Action<EventHandler> remove, Action<T, EventArgs> action) where T : class { SetAnyHandler<T, EventHandler, EventArgs>(subscriber, h => (o, e) => h(o, e), //This is a workaround from Rx add, remove, action); } }

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    I just realized my response to Alexandre never made it out of the "draft" status. Crazy. Well here it is/was...

    Clever code there Alexandre. I can see how it would certainly deal with cleaning up subscribers that no longer exist. I might be missing something, but in my case I have a ViewController with a child custom button on it and it looks like I would do something like this:

            this.SetAnyHandler(
    

    (e) => MyButton.TouchUpInside += e,
    (e) => MyButton.TouchUpInside -= e,
    HandleMyButtonTap);

    Did I get that right? I tried it..and the handler seems to be firing. But it ends up preventing GC anyway. Not sure why. Because its a custom child control maybe? Nothing is ever telling it to remove the handler when the view goes away, but since its a weak reference I didnt think that would be an issue. Unless there's a cycle I'm not seeing. Probably missing something obvious. For now I ended up moving the event subscriptions into ViewWillAppear and unsubscribe in ViewDidDisappear.

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    @rolf and @poupou, thanks for the thoughts. What about ReleaseDesignerOutlets? Is it considered a good practice to go ahead and override Dispose to call it? There is a good discussion here http://stackoverflow.com/questions/13050163/does-calling-releasedesigneroutlets-have-any-effect-on-monotouch-gc but wondering if the state of the art as it relates to ReleaseDesignerOutlets has changed at all.

  • RolfBjarneKvingeRolfBjarneKvinge USXamarin Team Xamurai

    Currently we recommend you don't bother with ReleaseDesignerOutlets. It used to make sense when viewDidUnload: was still working, but when Apple deprecated it and stopped calling it, it stopped making sense to use it.

  • GrigoryButeykoGrigoryButeyko RUMember
    edited July 2013

    I have similar problems. Here's the test controller I have. It is never freed. I do not understand what should I do to free it. I check the memory with Instruments (Allocations). I have 2 buttons in my test project. One presents this view controller, and the other calls GC.Collect.

    I click the first button to present VC, then close it with Done button, then click many times the second button. :)

    ` public partial class MemoryTest : UIViewController
    {
    public MemoryTest () : base ("MemoryTest", null)
    {
    NavigationItem.LeftBarButtonItem = new UIBarButtonItem (UIBarButtonSystemItem.Done);
    }
    private void left_button_clicked(object obj, EventArgs args)
    {
    NavigationController.PresentingViewController.DismissViewController(true, null);
    }
    public override void ViewWillAppear (bool animated)
    {
    base.ViewWillAppear (animated);
    NavigationItem.LeftBarButtonItem.Clicked += left_button_clicked;
    }
    public override void ViewWillDisappear (bool animated)
    {
    NavigationItem.LeftBarButtonItem.Clicked -= left_button_clicked;
    base.ViewWillDisappear (animated);
    }
    public override void DidReceiveMemoryWarning ()
    {
    base.DidReceiveMemoryWarning ();

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

    `

    Real help would be appreciated. I have similar issues with much larger project (nothing gets freed ever), so started to test from the scratch.

    UINavigationController nc = new UINavigationController(new MemoryVC()); main_nc.PresentViewController(nc, true, null);

    P.S. I have lots of experience writing with zero memory leaks in both Objective-C and C++, I thought I understand everything about memory.

  • GrigoryButeykoGrigoryButeyko RUMember
    edited July 2013

    Sorry I cannot make this forum to format my code. It just does not work. Coincidence?

  • RolfBjarneKvingeRolfBjarneKvinge USXamarin Team Xamurai

    @GrigoryButeyko: Do you repeatedly create MemoryTest instances and none are freed, or is it just one?

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    I've continued to struggle in certain situations finding the "right" thing to do. I've attached a similar example to what @GrigoryButeyko is asking about. In this case a table view controller is presented via button press. If you profile the app with the mono profiler you can see the MyTableViewController instances adding up with each button press. To "fix" the problem I've either had to not set the Action handles in the cells, or set the table source's handle back to the table to null. Either one allows GC to happen, but neither are ideal. In fact resetting the Action handle is really not very practical.

  • GrigoryButeykoGrigoryButeyko RUMember

    I have got invaluable advice from a friend. When I push the MemoryTes controller with NavigationController.PushViewController(new MemoryTest(), true); I can make it to be garbage collected if I add the following code to my test project: int count = this.NavigationController.ViewControllers.Length, recreating the list of ViewControllers.

    But I need the same workaround when presenting the MemoryTest modally, with the following code UINavigationController nc = new UINavigationController(new MemoryTest()); NavigationController.PresentViewController(nc, true, null);

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    @GrigoryButeyko, actually yeah looks like @BrendanZagaeski and I re-discovered that in a recent support effort: http://forums.xamarin.com/discussion/4953/harmless-looking-line-that-causes-controller-to-not-garbage-collect#latest, at least the part about resetting the ViewController array to workaround.

  • GrigoryButeykoGrigoryButeyko RUMember

    Ok, found workaround, if I rememeber the MemoryTest controller in the variable mem_vc before presenting, and then "after it is no more needed" I execute the following: "UINavigationController mnc = mem_vc.NavigationController;
    mem_vc = null;
    mnc.ViewControllers = new UIViewController[0];" but that is extremely annoying, as in the real project I do not easily have access to dismissed and forgotten view controller and moreover I really do not know "when it is no more needed". This is a major bug in mono touch... And very annoying as it is very old, 2 years! I'm sure the problem with managed counterpart of NSArray of something is generic enough to fix it?

  • GrigoryButeykoGrigoryButeyko RUMember

    Ok, obviously the culprit is the NSArray backing field here: public virtual UIViewController[] ViewControllers { [Export ("viewControllers", ArgumentSemantic.Copy)] get { UIApplication.EnsureUIThread (); UIViewController[] array; if (this.IsDirectBinding) { array = NSArray.ArrayFromHandle<UIViewController> (Messaging.IntPtr_objc_msgSend (base.Handle, Selector.GetHandle ("viewControllers"))); } else { array = NSArray.ArrayFromHandle<UIViewController> (Messaging.IntPtr_objc_msgSendSuper (base.SuperHandle, Selector.GetHandle ("viewControllers"))); } if (!NSObject.IsNewRefcountEnabled ()) { this.__mt_ViewControllers_var = array; } object arg_79_0 = (!UIDevice.CurrentDevice.CheckSystemVersion (5, 0)) ? null : this.ChildViewControllers; return array; }

    I thought Xamarin uses Mono Touch 6.0 with new refcounting, but quick check (printing NSObject.IsNewRefcountEnabled ()) revealed that it is false, so the backing field is used. Why? I checked both release and debug builds on both simulator and device.

  • GrigoryButeykoGrigoryButeyko RUMember

    Running the build with SGen on, as explained in http://spouliot.wordpress.com/2012/03/05/linker-vs-bindings-and-newrefcount/ and linker option set to true just crashes the project in SQLite.cs with the exception "Get method not found for property". If I disable linker, then the app will start and internal logic start working, but the UI is never updated and showing Default.png forever (but only sometimes! Sometimes it will start normally, so the SGen is absolute no go for real app yet).

    I will have to try subclass a Navigation Controller, add it to the list of all navigation controllers and periodically look is it attached to some other view controller and if not, then clean it up. :(

    I will continue reporting here on my quest for solving this problem. Thanks for reading. :)

  • AlexPalmaAlexPalma USMember ✭✭
    edited July 2013

    @DennisWelu, Actually your code is creating a permanent reference and that will prevent your object to be GC since the event will not be unregistered, try changing.

    this.SetAnyHandler( (e) => MyButton.TouchUpInside += e, (e) => MyButton.TouchUpInside -= e, HandleMyButtonTap);

    to

    this.SetAnyHandler( (e) => MyButton.TouchUpInside += e, (e) => MyButton.TouchUpInside -= e, (s, e) => s.HandleMyButtonTap(e));

    this is how it show be, if you notice the difference between the 2 is that on your code you where calling your method directly and creating a direct dependency to your object, on my example we create a lambda that receives 2 parameters one is the reference to the objectand the other the eventargs, an then we call the method using the object reference passed in instead of the this., cause of this there is no direct referencing and the object can be GC. The method may receive or not the eventargs, is up to you.
    On a side note if you have custom delegates like the one needed to manage collection changed events then they need to be implemented like this.

    this.SetAnyHandler<TypeOfTheClassThisRefersTo, NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>( h => new NotifyCollectionChangedEventHandler(h), h => collectionChanged.CollectionChanged += h, h => collectionChanged.CollectionChanged -= h, (s, e) => s.CollectionChanged(e));

    In this case we need to supply the converter function that receives a EventHandler and returns the NotifyCollectionChangedEventHandler delegate, we also need to specify all the types that compose the subscriber, the delegate and the eventargs.

    I hope know it makes more sense to you how this should be use and why needs to be use in a certain way.

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    @AlexandrePalma - thanks for clarifying and yeah that makes sense. I tried it out again and got it working with some caveats. I had a view where there were 3 event handlers. 2 of them were on standard UIKit objects, the 3rd was on a custom derived UI class, which I've learned from thread discussions and experience is where unsubscribing is pretty much required. If I just SetAnyHandler on the 2 standard objects my view seemed to be reclaimed by GC fine. If I tried setting it on the 3rd (custom) object it wasn't. The other thing I observed even when it "worked" was I still had 2 Action<MyView, System.EventArgs> hanging out there in memory, no matter how many times I went to the screen. Not sure I can explain that but can't really dig in further on it right now.

    In any case this is a nice tool to keep on the utility belt. I appreciate the code share and explanation! Thanks much.

  • AlexPalmaAlexPalma USMember ✭✭
    edited July 2013

    @DennisWelu, No prbs if you read the post on stackoverflow that I beased my extension methods on you will notice that Benjol points out the following.

    Note: in some circumstances, this CAN leave references to the wrapping classes created for the lambdas in memory, but they only weigh bytes, so I'm not too bothered.

    so I guess that is what you are seeing.

    as he said that doesn't bother me also that much, since the payoff is that the code is nice and clean and don't have to worry about if my class is being kept in memory cause someone (me or another dev on the team) forgot to unsubscribe to the event.

    If you find out who is referencing those wrapping classes let me know, is just that now i'm on a tight schedule and usually trying to pinpoint this things is can be very time consuming, o I may return to this once the project is release and I have more time to send on this.

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭
    edited July 2013

    @AlexandrePalma right on, that seems to be what I'm seeing. And agreed that its not a lot of bytes. The references seem to be coming from "Root Object" according to the profiler.

  • AlexPalmaAlexPalma USMember ✭✭

    @DennisWelu just out of curiosity everytime you close and then open the same view will create a new reference under the Root Object?

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    @AlexandrePalma no it just seems to create 2 references no matter how many view controller instances are created.

  • TommyBaggettTommyBaggett USUniversity ✭✭✭

    I've been following this thread and really enjoying the knowledge sharing. Thanks to everyone that has contributed so far!

    I came across this lengthy, but really good, article today. It's primary purpose is to explain why mobile web apps won't ever be as good as native apps, but also dedicates the last third of the article to C#, Java and other languages that depend on a garbage collector. There's even a quote from Miguel and multiple mentions of Xamarin in there.

    I'd love to hear your feedback and thoughts, especially on how it applies to memory management issues we face when using Xamarin for both iOS and Android app development.

    http://sealedabstract.com/rants/why-mobile-web-apps-are-slow/

    Cheers...

  • AlexPalmaAlexPalma USMember ✭✭
    edited July 2013

    @AlexWhite, Can't agree more to what you are saying, that was one of the things I didn't liked at the evolve sessions and on regular xamarin docs, is all examples are done using lambda expressions where they register to events using a lambda expression, of course people that start learning xamarin and don't know any better, they will follow the docs, and do this mistakes that then will cost many debugging hours.
    Most of the things is common sense like register and unregistered events, use weak references, and dispose any unnecessary big resources that no longer are needed like images, remember for Xamarin.Ios and image is only 4 bytes even that then you have a 2 MB file on the OS side.
    then there are some obscure things that looks like no one knows exactly what they are that can produce memory leaks, this was the things I would like to see a doc or white paper specifying situation that will produce memory leaks with in xamarin.

    just my 2 cents.

  • AlexWhiteAlexWhite GBMember ✭✭✭

    @AlexandrePalma thanks for your reply, you have a much better handle on this stuff than me. The weak event stuff certainly solves part of the problem, just need to do the same with NSAction events and this may solve the event blocking GC collection problem.

    So we try to move away from lambda's to gain a bit more control the following is a snip of code I use MT.D

    foreach (tblBook book in bookList) {
        BookStyledStringElement bk = new BookStyledStringElement(book,UITableViewCellStyle.Subtitle);
        var book2 = book;
        bk.AccessoryTapped += () => {
        _listList = new ListList (book2.BookId, book2.Description);
        NavigationController.PushViewController (_listList, true);
        };
        bk.Tapped += () => {
        _actionSheet = new UIActionSheet ("Actions");
            _actionSheet.AddButton ("Call book all");
        _actionSheet.AddButton ("Call book reset");
        _actionSheet.AddButton ("Call book completed");
    

    the above code you want to move into methods, the best place to put the methods is in the BookStyledStringElement class so that you can unhook the events when disposal happens, I found that you then have to pass the navigation controller stuff through to make it work, this seem very heavyweight to solve the problem, I have overridden the disposal stuff at both the BookStyledStringElement class and the cell class which I keep a reference to, because if I don't then the disposal method never gets hit.

    If I use instruments to see the heap, then with the lambda's in place just jumping into a form and back again can see the heap rise by a whopping 300k, take the code out of the lambda's and that rise is in the 80k region, this does tail off with successive repeats of going into the form and back out.

    A white paper written by someone who really understands this stuff would not take long, showing the correct way to code this stuff in both normal C# for IOS and against MT.D would improve things no end.

    ATB

    Alex

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    @TommyBaggett - I saw that article too and yeah, that's a pretty long read. :-) My feelings are that all dev of course involves tradeoffs. If time, money, and other considerations like long term maintenance werent an issue you'd write pure native to get optimal performance. But one or more of those things are big issues in most cases. Performance is a big part of the user experience but at some point it becomes irrelevant (e.g. at some point I dont really care if the tap events in my LOB app are handled in 1ms or 2ms, I won't notice the difference.) The article points out if you're doing mobile you should be and will be having to deal with mem management and perf issues one way or another. I'll trade off the plumbing work of completely manual mem management for a "reasonable" amount of work to get the garbage collector behaving in order to get "native/almost native" behavior/perf for a typical line-of-business app that has pretty good x-platform qualities. For certain types of apps the story would probably be different.

    I also note the only reference to Xamarin perf specifically was a quote about performance on original WP devices, which isn't really a strong statement I think. But in the end, GC has a perf effect and Miguel's quote underscores that it's a tradeoff by design.

    I agree with @AlexWhite and @AlexandrePalma that the memory management issue is currently the standout issue. As Alexandre rightly points out, as you learn you tend to follow the lead of examples and past .Net/C# habits until you find out "after the fact" that some things you should do differently. But if this were addressed either with compiler magic or clear recommendations up front to avoid pitfalls I don't have much else to complain about. :-)

  • AlexWhiteAlexWhite GBMember ✭✭✭

    I think we have multiple issues to address:-

    the easy ones are the click events for buttons, create the button don't use lambda's but use methods so

    button1.clicked += method;

    when disposing of the screen, recreating the button button1.clicked -=method works well.

    I have a few images that are used a lot throughout the apps, these I create the image in a static class load at startup they are only small but I don't want them repeated loaded.

    Then in the imageview I reference the cached image, this seems to work much better than repeatedly loading images and works well if you have a few images used a lot of times. I have not finished this bit of work but I am going to unhook the images when the imageview is disposed.

    I use a few customised styledstringelements, the cell for the class is declared at the class level so that I can get to it later, e.g. in the disposal for the class, I can then call dispose on the cell as well, not sure how well this is working, but stepping through the code indicates I am getting to all the overridden disposal methods so this looks like a good idea, without declaring at the class level the cell class never seems to get to the disposal method.

    Proper disposal of collections (of styledstringelements) esp if there is events hanging off them this is one area that I have not got a proper handle on, this has the potential of completely killing my apps, I have profiled one of my apps, it has over 1000 users, these users refresh the data on some of the screens 100's of times per day, each screen can list say 100's styledstringelements all with tapped and accessorytapped events on them, this area I know for sure is a problem the payload inside the lambda's, the difficultly here is inside the events I am doing stuff like navigationcontroller pushing, I can see the potential for circular references going on here so this is one area to try to really pin down as I guess I am not the only one navigating based on tapped or accessorytapped events, if we could use weak events here this might solve this problem.

    I don't mind the GC looking after this stuff as long as it eventually gets rid of all the old unused objects in memory, but having the ability to manually dispose of everything makes me feel like I have a bit more control.

    Unfortunately for me I don't have the heapshot app anymore as I am using the indie version of the tools now so all my debugging has to be done with instruments which is a lot more long winded than the heapshot app.

    All we need is a demo app of a few screens with a bunch of stuff on each screen with examples of proper creation and disposal of all the objects in each screen at all levels.

    Other than this issue, I love the xamarin stuff, it leverages my C# skills which I have been using from day one when Microsoft first beta'd it.

    ATB

    Alex

  • AlexPalmaAlexPalma USMember ✭✭

    @AlexWhite, I don't see why you will have circular references if you use weak references, IMO everything that is pass to a class and is not manage by her should use weak references.

  • AlexWhiteAlexWhite GBMember ✭✭✭

    I have not found out how to get the tapped event over to a weak event as it uses nsaction rather than the eventhandler stuff if you see what I mean, more of a lack of knowledge on my part than anything with this bit of the problem, but inside my events I am calling other forms. What I do know is the payload that seems to be left behind is from the tapped event lambda, of course I want to move away from the lambda but I am not happy passing the navigationcontroller stuff into the classes that would have the event in it, seems too heavy weight.

    ATB

    Alex

  • AlexPalmaAlexPalma USMember ✭✭

    Alex that's exactly why I'm using Mvvm Cross, so that you don't have to deal with all that plumbing, all my navigation is done at the ViewModel leveland I just bind the taped events to ICommands of the ViewModel, Also MvvmCross has implemented weak events subscription also.

  • AlexWhiteAlexWhite GBMember ✭✭✭

    You have convinced me to have a look @ MvvM cross, it was going to be a future project but your post has just bumped it up the list, I am guessing it is a direct competitor to MT.D (and more) just when I was getting comfortable with it.

    Thanks for the post.

    ATB

    Alex

  • AlexPalmaAlexPalma USMember ✭✭

    @AlexWhite, your welcome, but you can still work with MT.D, mvvm cross support it, so you don't have to start from scratch. one thing good about mvvmcross is that @slodge has a great blog explaining how you can do everything with mvvm cross, in case of dialogs take a look to this post

  • DennisWeluDennisWelu USUniversity, Developer Group Leader ✭✭

    At the risk of making a long thread longer...I ran across this Xamarin doc (again) about mem management and best practices and didn't include it in my original list of resources so to be fair here it is: http://docs.xamarin.com/guides/cross-platform/application_fundamentals/memory_perf_best_practices

    That actually is a good primer on the types of things to look for and the "why" behind them. Perhaps evolving that into more concrete and consumable practices, especially for the novice user, would be helpful. There's tidbits of code examples in there but it's still a fairly abstract read. Maybe nice to enumerate the "what" from the "how" (and maybe cross reference between them) so I can use it as a checklist. Also I think eyes tend to glaze over in the "why" part and "I want to just get coding" so just show me what to do and not do. At the same time I realize there's a certain amount of complexity that just cant be escaped on this issue.

  • MatteoMonizzaMatteoMonizza ITBeta ✭✭

    I agree with asp_net

  • AlexWhiteAlexWhite GBMember ✭✭✭

    asp_net,

    I hear you and agree, I now have two apps out in the field one with 3,000+ active users and the other with about 2,000 users, there is about 1,500 users that have both apps, both apps get hammered every day by everyone, approx user time per day is 2-3 hours usage, these are bread and butter for these users and if there are issues I find out very quickly.

    Rough figures are 1-2 users per day crash in either app and restarting the app everything works fine again for days/weeks/months and if I suggest closing other apps down whilst using them the crashes are even less, so it is a creeping memory issue over time, the crashes happen at different points in the app so it is not code and normally the crashes happen when a view controller is being closed.

    I would love to get to the bottom of these issues but I have resigned myself to making very untechnical comments to the users of "just close the app every few days and restart it".

    As you say a demo complex memory app with all the "right" ways to do everything in terms of memory would not take someone at xamarin a lot of time and would save us all a lot of headaches.

    All The Best

    alex

  • Sreeraj.0276Sreeraj.0276 USMember ✭✭

    Hi,
    I thank everyone for this super informative thread and tips on memory management in Xamarin.Ios. Really helpful one for starters. I am struggling with memory issues in my app.

    I have changed all the Lambda delegates in my ViewControllers to Event Handler methods so that I can dereference them later. I tried dereferencing the methods in ViewWillDisappear. But when I debugged, I learnt that none of the methods -> ViewWillDisappear, ViewDidDisappear, ViewDidUnload or Dispose was getting invoked when I am navigating. I have read that ViewDidUnload have been deprecated. But what about other methods ? Where should I be dereferencing ?

    One of my View Controller has a UICollectionView. I think first culprit is in it's UICollectionViewSource. I could not get ItemSelected callback to work on UICollectionViewDelegateFlowLayout or UICollectionViewSource while testing on my device. It works fine in simulator though. When I test on device neither of ShouldHighlightItem, ItemHighlighted, ShouldSelectItem, ItemSelected is invoked. So I had to wire up a UITapGestureRecognizer to Cell in the GetCell method.
    Is this likely to create a leak ? If so, what is the work around ?

    I have wrapped all UIImage declarations in Using statements.

    I have used like 10 UIPanGestureRecogniser in total in my application. Do I need to dereference them too ?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    But when I debugged, I learnt that none of the methods -> ViewWillDisappear, ViewDidDisappear, ViewDidUnload or Dispose was getting invoked when I am navigating.

    It's hard to know exactly what you mean by "navigating", but if your view is leaving the screen then ViewWillDisappear and ViewDidDisappear should get called. ViewDidUnload will never be called, and you should not implemented it. Dispose(bool) would only be called if you call Dispose() directly or if your object is finalized (i.e., it is about to be garbage collected). You can't use Dispose(bool) to unsubscribe hoping to prevent leaks. I wrote a whole post about finalizers and IDisposable recently that you may find interesting (it's not Xamarin specific).

    If your View(Will|Did)Disappear methods are not being called then something fishy is going on. Maybe you're not overriding these methods in a view controller? Maybe you haven't set up your too view controller correctly? It's hard tos ay without a full code example. Try attaching a zip with a working project that reproduces this behavior.

    Your collection view issue seems entirely unrelated so you should just make another post for it.

Sign In or Register to comment.