2010-10-13 12 views
2

J'ai un problème de mémoire dans mon application iPhone. J'utilise MonoTouch. J'ai cherché le problème en utilisant un compteur d'instance statique. Le problème a quelque chose à voir avec les contrôleurs de vue modale. Lorsque je navigue d'un contrôleur de vue racine vers un contrôleur de premier niveau et vice-versa, je trouve que le contrôleur de premier niveau est collecté par le garbage. Mais quand je fais le modal de premier niveau-viewcontroller en appelant PresentModalViewController, et je reviens en appelant DismissModalViewControllerAnimated, je trouve que le contrôleur de premier niveau-vue n'est pas recueilli. Pas même quand j'appelle GC.Collect().UIViewController n'est pas récupéré lors du démarrage modal

Pourquoi pas? Est-ce que je fais quelque chose de mal?

Quelle est la meilleure pratique pour assurer la libération des contrôleurs de vue?

partial class RootViewController : UITableViewController 
{ 
    static int instanceCount; 
    static int nextId; 
    int instanceId; 

    public RootViewController (IntPtr handle) : base(handle) 
    { 
     instanceCount++; 
     instanceId = nextId++; 
     Console.WriteLine(string.Format("RootViewController #{0} Count={1}", instanceId, instanceCount)); 
    } 

    public override void ViewDidLoad() 
    { 
     base.ViewDidLoad(); 
     Title = "Root"; 
     NavigationItem.RightBarButtonItem = new UIBarButtonItem("ModalVC", UIBarButtonItemStyle.Plain, 
      delegate 
      { 
       var firstlevelVc = new FirstLevelViewController(); 
       PresentModalViewController(new UINavigationController(firstlevelVc), true); 
      }); 

     NavigationItem.LeftBarButtonItem = new UIBarButtonItem("PushVC", UIBarButtonItemStyle.Plain, 
      delegate 
      { 
       var firstlevelVc = new FirstLevelViewController(); 
       NavigationController.PushViewController(firstlevelVc, true); 
      }); 
    } 

    public override void ViewDidAppear (bool animated) 
    { 
     base.ViewDidAppear (animated); 
     GC.Collect(); 
    } 

    ~RootViewController() 
    { 
     instanceCount--; 
     Console.WriteLine(string.Format("RootViewController #{0} Count={1}", instanceId, instanceCount)); 

    } 
} 
public partial class FirstLevelViewController : UIViewController 
{ 
    static int instanceCount; 
    static int nextId; 
    int instanceId; 
    public FirstLevelViewController (IntPtr handle) : base(handle) 
    { 
     Initialize(); 
    } 

    [Export("initWithCoder:")] 
    public FirstLevelViewController (NSCoder coder) : base(coder) 
    { 
     Initialize(); 
    } 

    public FirstLevelViewController() : base("FirstLevelViewController", null) 
    { 
     Initialize(); 
    } 

    void Initialize() 
    { 
     instanceCount++; 
     instanceId = nextId++; 
     Console.WriteLine(string.Format("FirstLevelViewController #{0} Count={1}", instanceId, instanceCount)); 
    } 

    public override void ViewDidLoad() 
    { 
     base.ViewDidLoad(); 
     Title = "1. level"; 

     NavigationItem.RightBarButtonItem = new UIBarButtonItem("Dismiss modal", 
                   UIBarButtonItemStyle.Plain, 
                   delegate { ParentViewController.DismissModalViewControllerAnimated(true); }); 

    } 

    ~FirstLevelViewController() 
    { 
     instanceCount--; 
     Console.WriteLine(string.Format("FirstLevelViewController #{0} Count={1}", instanceId, instanceCount)); 

    } 
} 
+0

Je ne peux pas répliquer ceci, mais je n'ai pas vos xibs ou code appelant. Pourriez-vous m'envoyer votre projet entier ou déposer un bug sur http://monotouch.net/Support? –

Répondre

0

Finalizer (~ méthode) est appelée lorsque le compteur de référence sur un objet est à 0, vous êtes sûr que vous ne disposez pas d'une référence quelque part à l'UIViewController utilisez-vous? Pour être sûr que je n'ai pas de référence et m'assurer du retrait du GC, j'implémente la méthode IDisposable et Dispose, je l'appelle et je fais le nettoyage nécessaire (enlever tous les objets des collections internes et autres) et je règle la variable d'origine à null. Donc, il ressemble à:

MyUIViewController cntrl = new MyUIViewController();

... faire des choses

// lorsque vous avez terminé cntrl.Dispose(); cntrl = null;

Et c'est tout.

BTW. Je crois que vous ne devriez pas appeler GC.Collect par vous-même que fréquemment, car il arrête tous les threads et ne fait que le nettoyage, tout le travail est à ce moment arrêté et est assez lourd sur les ressources.

Espérons que ça aide.

+0

S'il y a une référence à FirstLevelViewController, cela pourrait provenir du UINavigationController créé avec l'instruction PresentModalViewController (new UINavigationController (firstlevelVc), true). Mais autant que je peux voir le contrôleur de navigation est inaccessible. –

+0

Je n'utilise que le finaliseur pour le compte d'instance et trace et j'utilise seulement GC.Collect() pour prouver mon point. –

+1

Et comment écartez-vous le contrôleur modal? Le modèle doit l'appeler du contrôleur enfant vers le premier avec this.ParentViewController.DismissModalViewControllerAnimated. L'appeler former l'enfant sur lui-même pourrait provoquer cela. Juste une pensée. –

0

Si vous souhaitez éliminer le contrôleur manuellement, vous pouvez appeler Dispose sur celui-ci.