开发者

Why would MonoTouch not garbage collect my custom UIButton unless I call button.RemoveFromSuperview()?

There seems to be something holding a reference to my custom button, MyButton (which inherits from UIButton), causing it not to be garbage collected unless I remove it from the superview. This, in turn, would cause the view controller that it is on to also not be finalized and collected.

In my example, I have my custom button but I also have a standard UIButton on the view controller which does not need to be removed from the superview in order to be collected. What's the difference? Looks pretty similar to me.

See this code. The irrelevant lines were removed for example's sake. Some things to note about the sample:

-MyButton is pretty empty. Just a constructor and nothing else overridden.

-Imagine MyViewController being on a UINavigationController

-LoadView() just creates the buttons, hooks up an event for each and adds it to the view

-Touching _button would push another MyViewController to the nav controller

-I'm doing some reference cleanup when popping view controllers off the nav controller in ViewDidAppear()

-In CleanUpRefs() you'll see that I have to remove _myButton from superview in order for all the objects to be garbage collected. _button, on the other hand does not need to be removed.

-I'm expecting the entire MyViewController to be collected, including all subviews, when popping from the nav controller but commenting out _myButton.RemoveFromSuperview() stops this from happening.

public class MyViewController : UIViewController
{
   private UIButton _button;
   private MyButton _myButton;
   private MyViewController _nextController;

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

      _button = UIButton.FromType(UIButtonType.RoundedRect);
      _button.TouchUpInside += PushNewController;
      View.AddSubview(_button);

      _myButton = new MyButton();
      _myButton.TouchUpInside += MyButtonTouched;
      View.AddSubview(_myButton);
   }

   private void PushNewController(object sender, EventArgs e)
   {
      _nextController = new MyViewController();
      NavigationController.PushViewController(_nextController, true);
   }

   private void MyButtonTouched(object sender, EventArgs e)
   {
      Console.WriteLine("MyButton touched");
   }

   public void CleanUpRefs()
   {
      //_button.RemoveFromSuperview();
      _myButton.RemoveFromSuperview();
      // remove reference from hooking up event handler
      _button.TouchUpInside -= PushNewController;
      _myButton.TouchUpInside -= MyButtonTouched;
      _button = null;
      _myButton = null;
   }

开发者_开发知识库   public override void ViewDidAppear(bool animated)
   {
      base.ViewDidAppear(animated);

      if(_nextController != null)
      {
         _nextController.CleanUpRefs();
         _nextController = null;
      }
   }
}

It seems as if there's something different with the fact that MyButton isn't a straight UIButton in that it is inherited. But then again, why would there be an extra reference count to it that's being removed by calling RemoveFromSuperview() especially when there's a UIButton just like it that doesn't need to be removed?

(I apologize for the really bad layout, stackoverflow seems to have problems laying out bullets right above code snippets)

Update: I filed a bug report with the MonoTouch team. You can download the sample project from there if you want to run it. Bug 92.


The reason for not garbage collecting in that scenario is just a bug in MonoTouch.

The upcoming MonoTouch release will contain a fix for this. If you are in a hurry, you can replace your /Developer/MonoTouch/usr/lib/mono/2.1/monotouch.dll with the copy I placed here:

http://tirania.org/tmp/monotouch.dll

I would make a backup, in case I did something wrong in my work-in-progress library.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜