Bug 1939 - Derived UIButton not being disposed
Summary: Derived UIButton not being disposed
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime ()
Version: 5.0
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Rolf Bjarne Kvinge [MSFT]
Depends on:
Reported: 2011-11-08 10:18 UTC by ben.chester.bugzilla
Modified: 2012-02-22 18:51 UTC (History)
5 users (show)

Is this bug a regression?: ---
Last known good build:

Sample project demonstrating the issue (1.46 MB, application/zip)
2011-11-08 10:18 UTC, ben.chester.bugzilla

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description ben.chester.bugzilla 2011-11-08 10:18:47 UTC
Created attachment 843 [details]
Sample project demonstrating the issue

I am having problems with a class derived from UIButton and garbage collection. In the sample project I have created a very simple class derived from UIButton. Apart from the derivation, the class is empty. I then have a derived UIViewController which adds an instance of the button in ViewDidLoad() and wires it up to an event handler. The event handler pushes a new instance of the view controller class onto the navigation stack. I have also overridden Dispose(bool) on the view controller so that it writes to the console when it is being disposed, and then proceeds as usual.

The type of button can be easily changed by replacing the constructor on line 26 of DerivedButtonTestViewController.cs with one for the base class (UIButton). It is observing the difference between these two cases that exposes the strange behaviour.

In order to see the difference in behaviour, I run the app in the simulator, tap the horrible green button (sorry about the colour scheme), tap the back button once the new view has loaded, and then tap the green button again. When using the base UIButton class, I then get a console output saying that a view controller has been disposed. When using my custom button class, no such message arrives.

This causes memory problems in a more complex app I am writing with a similar basic structure. Am I missing something or is this a bug?
Comment 1 Sebastien Pouliot 2011-11-09 14:08:13 UTC
fwiw it did sounds familiar, see http://bugzilla.xamarin.com/show_bug.cgi?id=57
but that patch is still there (may be similar, but not a regression)
Comment 2 ben.chester.bugzilla 2011-12-07 04:50:07 UTC
I have done a little more testing on this and discovered that if the derived button is removed from its superview in ViewDidDisappear, the view controller is disposed correctly.
Comment 3 Rolf Bjarne Kvinge [MSFT] 2012-02-22 18:51:58 UTC
This is an unfortunate side-effect of mixing ObjC (refcounting) and managed (garbage collection) code.

It's a complicated topic, but the issue is circular references crossing different memory management types: For user-defined types (DerivedButton for instance), MonoTouch will ensure that the object is not freed unless the native refcount reaches 1 (which is the reference managed code has, and at this point MonoTouch knows that nobody else has a reference to the object). For this sample, DerivedButton will have a refcount of 2 (one managed reference, and one from DerivedButtonTestViewController.View) - so it will not be freed. For DerivedButton to be freed, DerivedButtonTestViewController.View has to be freed (thus releasing its reference to it), and for that to happen DerivedButtonTestViewController has to be freed (thus releasing its reference to the View). But DerivedButtonTestViewController will not be freed, because DerivedButton has a (managed) reference to it (due to this line: "button.TouchUpInside += this.OnButtonTapped" - if you change OnButtonTapped to be a static method, thus breaking the cycle, you'll see that the controller is freed).

Unfortunately we've determined that it's pretty much impossible for MonoTouch to resolve this scenario, help is needed from the developer (and that help is called Dispose - if you Dispose the controller when you're done with it, the link between the controller -> view -> button).