2012-12-12 3 views
14

Ich habe einen benutzerdefinierten Container UIViewController mit sechs untergeordneten UIViewControllern und einer Reihe von Registerkarten, mit denen der Benutzer interagiert, um zwischen den untergeordneten View-Controllern zu wechseln. Das Problem ist, wenn mein Container-View-Controller freigegeben ist, sind die Child-View-Controller nicht.Container UIViewController gibt seine untergeordneten View-Controller nicht frei

Ich habe überprüft, dass die Child View-Controller nicht durch Hinzufügen von Debugging-Code zu ihren Dealloc-Methoden freigegeben werden, und sie sind freigegeben, solange ihre Ansichten der Containeransicht Controller-Ansicht nicht hinzugefügt werden.

Im Folgenden finden Sie einen Auszug aus dem Code, mit dem ich meinen benutzerdefinierten Containeransicht-Controller erstelle. Die viewController-Zeiger sind iVars. Ich benutze auch ARC, deshalb gibt es keine Release-Anrufe.

- (void)init 
{ 
    if ((self = [super init])) { 
     vc1 = [[UIViewController alloc] init]; 
     [self addChildViewController:vc1]; 

     vc2 = [[UIViewController alloc] init]; 
     [self addChildViewController:vc2]; 

     vc3 = [[UIViewController alloc] init]; 
     [self addChildViewController:vc3]; 

     vc4 = [[UIViewController alloc] init]; 
     [self addChildViewController:vc4]; 

     vc5 = [[UIViewController alloc] init]; 
     [self addChildViewController:vc5]; 

     vc6 = [[UIViewController alloc] init]; 
     [self addChildViewController:vc6]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [vc1 removeFromParentViewController]; 
    vc1 = nil; 

    [vc2 removeFromParentViewController]; 
    vc2 = nil; 

    [vc3 removeFromParentViewController]; 
    vc3 = nil; 

    [vc4 removeFromParentViewController]; 
    vc4 = nil; 

    [vc5 removeFromParentViewController]; 
    vc5 = nil; 

    [vc6 removeFromParentViewController]; 
    vc6 = nil; 
} 

- (void)switchFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController 
{ 
    if (fromViewController) { 
     [fromViewController.view removeFromSuperview]; 
    } 

    [self.view addSubview:toViewController]; 
    toViewController.view.frame = self.view.bounds; 
} 

Haben Sie irgendwelche Ideen, was ich falsch mache?

+0

Aus Neugier, verweisen Sie Registerkarten, die steuern, welches Kind dargestellt wird. Wie hast du das umgesetzt? Ich frage, weil ich sehe, dass der 'frame' des Kindes auf 'self.view.frame' gesetzt ist, also verstehe ich nicht, wo in der Ansicht des Elternteils Sie dieses Tab-Control anzeigen würden. – Rob

+0

Das war nur ein super schnelles Beispiel dessen, was mein tatsächlicher Code macht, also ist es nicht 100% durchdacht. In meiner praktischen Anwendung gibt es Tabs, die vertikal entlang der Seite laufen, und dann gibt es eine Ansicht innerhalb des Hauptansicht-Controllers, in die die Ansichten des Kind-View-Controllers eingefügt werden, und dann wird ihr Frame auf childViewController.frame = contentContainerView.bounds gesetzt; –

+0

Das macht Sinn. Auf den ersten Blick, als ich mir ein Beispiel von Eindämmung ansah, wo es den ganzen Bildschirm einnahm, fragte ich mich, ob Eindämmung nötig war. Hört sich an, als hättest du einen guten Plan. Tut mir leid, dich zu belästigen. ;) – Rob

Antwort

11

Wie ich vermuten, das Problem im Zusammenhang hinzuzufügen ist nicht auf der View Controller Containment Code in der Frage, sondern eher das Hinzufügen der Beobachter (die Sie in Ihrer Antwort auf diese Frage diskutieren):

[[NSNotificationCenter defaultCenter] addObserverForName:kUpdateEventName object:nil queue:nil usingBlock:^(NSNotification *note) { 
    // do stuff when update happen 
}]; 

Und dass Sie versucht haben, es also mit

[[NSNotificationCenter defaultCenter] removeObserver:self name:kUpdateEventName object:nil]; 

zu entfernen, gibt es zwei Probleme:

  1. Wenn Sie addObserverForName:object:queue: verwenden, ist dies nicht der richtige Weg, um diesen Beobachter zu entfernen. Stattdessen definieren eine Eigenschaft Spur des Betrachters zu halten:

    @property (nonatomic, weak) id<NSObject> notification; 
    

    Dann auf diesen Beobachter eine Referenz speichern, wenn Sie es schaffen:

    self.notification = [[NSNotificationCenter defaultCenter] addObserverForName:kUpdateEventName object:nil queue:nil usingBlock:^(NSNotification *note) { 
        // do something 
    }]; 
    

    Und wenn Sie es entfernen wollen, benutzen Sie diese Referenz:

    [[NSNotificationCenter defaultCenter] removeObserver:self.notification]; 
    

    Das würde sicherstellen, dass der Beobachter ordnungsgemäß entfernt würde.

  2. Der Fehler, dass die untergeordneten Ansichtscontroller freigegeben wurden, während dieser Beobachter vorhanden war, bedeutet, dass dieser Block, der an addObserverForName:object:queue: übergeben wurde, einen Verweis auf self hatte. Wenn Sie versucht haben, diesen Observer korrekt zu entfernen (wie oben gezeigt), haben Sie in dealloc immer noch einen starken Referenzzyklus (früher bekannt als Retain-Zyklus).Dies wird in einer Reihe von Möglichkeiten, gelöst, aber das robusteste Muster ist der starken Referenzzyklus in erster Linie zu verhindern, indem sie mit dem weakSelf Muster:

    typeof(self) __weak weakSelf = self; 
    
    self.notification = [[NSNotificationCenter defaultCenter] addObserverForName:kFooNotification object:nil queue:nil usingBlock:^(NSNotification *note) { 
        // use `weakSelf` in this block; not `self` 
    }]; 
    

Meine ursprüngliche Antwort ist unten:


Während Srikanth ist richtig, dass nach addChildViewController, sollten Sie didMoveToParentViewController:self anrufen und vor removeFromParentViewController sollten Sienennen. Aber das ist nicht dein Problem. In der Tat habe ich eine Variation Ihres Codes (auch ohne die dealloc), und die Kinder-Controller sind gut freigegeben.

Unterm Strich vermute ich, dass Ihr Problem woanders ruht, wahrscheinlich einen Retain-Zyklus irgendwo. Zum Beispiel, haben Kinder einen starken Bezug zum Elternteil? Verwenden Sie wiederkehrende Timer? Sie verweisen auf einige Tabs. Sie verwenden keinen Tab-Controller, oder? Es muss so etwas sein.

[zur Revisionshistorie finden Sie, wenn Sie Rest der ursprünglichen Antwort sehen wollen, mit Code-Snippets und kleinere Details zu dem Codebeispiel des OP]

+1

Danke für den Info-Mann.Yeah, nachdem du erwähnt hast, dass der ursprüngliche Code, den ich gepostet habe, die Kinder losgelassen habe, habe ich beschlossen, meine Kind-View-Controller-Klasse in eine neue Subklasse zu wechseln, die eigentlich nichts anderes tut, als den Titel während Dealloc auszuloggen ... genau wie du hat getan. Sobald ich das gemacht habe, begannen die Kindersicht-Controller korrekt zu releasen. Also ich denke ich habe ein Problem woanders. Danke nochmal für die Hilfe! –

24

Dies ist nicht die Art und Weise Controller Kind Ansicht hinzuzufügen und zu entfernen

[childViewController willMoveToParentViewController:nil]; 
    [childViewController view] removeFromSuperview]; 
    [childViewController removeFromParentViewController]; 

ist die Art und Weise zu entfernen, und es ist

[parentViewController addChildViewController:childViewController]; 
    [parentViewController.view addSubview:childViewController.view]; 
    [childViewController didMoveToParentViewController:parentViewController]; 
+2

Zweite Zeile im Entfernungscode fehlt eine Initiale [ – crgt

2

Nach Stunden und Stunden zu versuchen, auf ich, um herauszufinden, was schließlich vorging Ich habe festgestellt, was meine Kind-View-Controller dazu veranlasst hat, korrekt zu veröffentlichen.

Jeder View-Controller hatte in jedem die folgende Benachrichtigung, so dass er auf verschiedene Ereignisse reagieren konnte.

[[NSNotificationCenter defaultCenter] addObserverForName:UPDATE_EVENT object:nil queue:nil usingBlock:^(NSNotification *note) { 
    // do stuff when update happen 
}]; 

Stellt sich aus irgendeinem Grund aus, die durch die Freisetzung richtig meine Ansicht-Controller wurde zu halten. Ich vermute, dass dies den View-Controller zur NSNotificationCenters-Beobachterliste hinzugefügt hat und nicht entfernt wurde, als ich die folgende Zeile machte.

[[NSNotificationCenter defaultCenter] removeObserver:self name:UPDATE_EVENT object:nil]; 

Also um mein Problem zu beheben, habe ich nur die Benachrichtigung geändert, um wie unten zu registrieren.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateEvent:) name:UPDATE_EVENT object:nil]; 

Ich habe keine Ahnung, warum die Art, wie ich die Benachrichtigung wurde die Registrierung nicht war meine Ansicht-Controller ermöglicht richtig zu lösen, aber dies schien es behoben zu haben. Wenn jemand einen Einblick hat, warum dieses Problem passieren würde, lassen Sie es mich bitte wissen.

Danke!

+2

Ich glaube, dass der Block "Do stuff when update happen", der an den Beobachter angehängt ist, über einen nicht blocksicheren Verweis auf "self" schließt und die Referenz leckt. – jdc

Verwandte Themen