2012-08-15 5 views
9

Ich habe viele Beiträge auf Stack-Überlauf gesehen, dass die ViewDidLoad-Methode der Controller nur beim ersten Zugriff auf den Controller aufgerufen wird und nicht unbedingt jedes Mal, aber immer bei wenigstens einmal.ViewDidLoad wird tatsächlich jedes Mal aufgerufen, wenn es einen Übergang gibt

Das sehe ich überhaupt nicht! Ich stelle einen einfachen Test zusammen, um dies hervorzuheben: https://github.com/imuz/ViewDidLoadTest

Es scheint für Navigation Controller-Segmente und modale Ansichten viewDidLoad wird immer aufgerufen. Der einzige Zeitpunkt, an dem es nicht aufgerufen wird, ist der Wechsel zwischen den Tabs.

Jede Erklärung viewDidLoad I Dies steht im Widerspruch finden:

Und Äpfel eigene Dokumentation zeigen, dass eine Ansicht nur entladen, wenn Speicher niedrig ist .

Ich mache gerade die Initialisierung in viewDidLoad unter der Annahme, dass es mit jedem Übergang aufgerufen wird.

Fehle ich hier etwas?

Antwort

11

Ich glaube, die Apple-Dokumentation beschreibt eine Situation, in der der View-Controller nicht freigegeben wird. Wenn Sie ein Segment verwenden, verursachen Sie die Instanziierung eines neuen Zielcontrollers und müssen als neues Objekt eine Ansicht laden.

In xib-basierten Anwendungen habe ich manchmal ein Controller-Objekt zwischengespeichert, von dem ich wusste, dass ich es häufig wiederverwenden könnte. In diesen Fällen verhielten sie sich in Übereinstimmung mit der Dokumentation in Bezug darauf, wann eine Ansicht geladen werden musste.

Bearbeiten: Beim Lesen der Links, die Sie enthalten, sehe ich keinen Widerspruch in ihnen. Sie sprechen auch über Dinge, die während der Lebensdauer eines View-Controller-Objekts auftreten.

+0

Es ist die Bits, wo sie über Ansichten sprechen, die auf 'Niedrigspeicherbedingungen' entladen werden, was impliziert, dass sie standardmäßig herumliegen, was ich nicht als den Fall gesehen habe. Es hängt also wirklich von der Implementierung des übergeordneten Controllers ab. Wenn Sie Navigationscontroller verwenden, wird jedes Mal eine neue Instanz erstellt und geladen. Nicht so mit Tabcontrollern. – Imran

+1

Eine Möglichkeit, das geringe Speicherverhalten (auf dem Simulator) zu testen, besteht darin, einen View-Controller aufzustellen, ihn mit einem Modal View-Controller zu überdecken und die Hardware-> Simulate Memory Warning-Option zu verwenden. Die Sicht des versteckten Controllers sollte entladen und dann erneut geladen werden, wenn der modale Befehl beendet wird. –

+0

oic interessant, krank versuchen, dass. Ich denke, dass jede Ansicht, die momentan nicht aktiv ist, ein Kandidat für das Entladen ist. – Imran

0

Sie wird jedes Mal aufgerufen, wenn die Ansicht des Controllers von Grund auf neu geladen wird (d. H. Angefordert, aber noch nicht verfügbar). Wenn Sie den Controller freigeben und die Ansicht damit einhergeht, wird er beim nächsten Instanziieren des Controllers erneut aufgerufen (z. B. wenn Sie den Controller erstellen, um ihn modal oder über das Segment zu schieben). View-Controller in Registerkarten werden nicht freigegeben, da der Tab-Controller sie beibehält.

12

Die Antwort von Phillip Mills ist korrekt. Dies ist nur eine Verbesserung davon.

Das System funktioniert wie dokumentiert.

Sie sehen viewDidLoad, weil der View-Controller, der auf den Navigationscontroller geschoben wird, eine neue Instanz ist. Es muss viewDidLoad aufrufen.

Wenn Sie etwas weiter nachforschen, würden Sie sehen, dass jeder dieser View-Controller freigegeben wird, wenn sie geclippt werden (setzen Sie einfach einen Breakpoint oder NSLog in dealloc). Diese Freigabe hat nichts mit dem View-Controller-Container zu tun ... sie steuert nicht die Lebensdauer des Controllers, den sie verwendet ... sie enthält nur einen starken Hinweis darauf.

Wenn der Controller vom Navigationssteuerungsstapel entnommen wird, gibt der Nav-Controller seine Referenz frei, und da keine weiteren Verweise darauf vorhanden sind, wird der View-Controller die Zuordnung aufheben.

Der Navigationscontroller enthält nur starke Referenzen zum Anzeigen von Controllern, die sich in seinem aktiven Stapel befinden.

Wenn Sie den gleichen Controller wiederverwenden möchten, sind Sie verantwortlich für die Wiederverwendung. Wenn Sie Storyboard-Übergänge verwenden, geben Sie diese Kontrolle (weitgehend) auf.

Nehmen wir an, Sie haben ein push Segment, um Controller Foo als Ergebnis des Tippens einer Schaltfläche anzuzeigen. Wenn diese Schaltfläche angetippt wird, erstellt "das System" eine Instanz von Foo (der Zielansicht-Controller) und führt dann den Übergang aus. Der Controller-Container enthält jetzt die einzige starke Referenz auf diesen Ansichtscontroller. Sobald es damit fertig ist, wird der VC dealloc.

Da jedes Mal ein neuer Controller erstellt wird, wird viewDidLoad jedes Mal aufgerufen, wenn der Controller angezeigt wird.

Wenn Sie dieses Verhalten ändern und den View-Controller zur späteren Wiederverwendung zwischenspeichern möchten, müssen Sie dies speziell tun. Wenn Sie keine Storyboard-Übergänge verwenden, ist es einfach, da Sie den VC tatsächlich zum Nav-Controller schieben/poppen.

Wenn Sie jedoch Storyboard-Segmente verwenden, ist es ein bisschen mehr Ärger.

Es gibt eine Reihe von Möglichkeiten, dies zu tun, aber alle erfordern eine Art von Hacking. Das Storyboard selbst ist dafür verantwortlich, neue View-Controller zu instanziieren. Eine Möglichkeit besteht darin, instantiateViewControllerWithIdentifier zu überschreiben. Das ist die Methode, die aufgerufen wird, wenn ein Segment einen View-Controller erstellen muss. Es wird sogar für Controller aufgerufen, denen Sie keine Kennung zuweisen (das System stellt eine erfundene eindeutige Kennung bereit, wenn Sie keine zuweisen).

Hinweis, ich hoffe, dies ist vor allem für Bildungszwecke. Ich behaupte nicht, dass dies der beste Weg ist, um Ihre Probleme zu lösen, was auch immer sie sein mögen.

So etwas wie ...

@interface MyStoryboard : UIStoryboard 
@property BOOL shouldUseCache; 
- (void)evict:(NSString*)identifier; 
- (void)purge; 
@end 
@implementation MyStoryboard 
- (NSMutableDictionary*)cache { 
    static char const kCacheKey[1]; 
    NSMutableDictionary *cache = objc_getAssociatedObject(self, kCacheKey); 
    if (nil == cache) { 
     cache = [NSMutableDictionary dictionary]; 
     objc_setAssociatedObject(self, kCacheKey, cache, OBJC_ASSOCIATION_RETAIN); 
    } 
    return cache; 
} 
- (void)evict:(NSString *)identifier { 
    [[self cache] removeObjectForKey:identifier]; 
} 
- (void)purge { 
    [[self cache] removeAllObjects]; 
} 
- (id)instantiateViewControllerWithIdentifier:(NSString *)identifier { 
    if (!self.shouldUseCache) { 
     return [super instantiateViewControllerWithIdentifier:identifier]; 
    } 
    NSMutableDictionary *cache = [self cache]; 
    id result = [cache objectForKey:identifier]; 
    if (result) return result; 
    result = [super instantiateViewControllerWithIdentifier:identifier]; 
    [cache setObject:result forKey:identifier]; 
    return result; 
} 
@end 

Nun haben Sie das Storyboard verwenden. Während die UIApplication auf dem Haupt-Storyboard gespeichert ist, stellt sie leider keine API bereit, um sie zu erhalten. Jeder View-Controller verfügt jedoch über eine Methode storyboard, um das Storyboard zu erhalten, aus dem er erstellt wurde.

Wenn Sie Ihre eigenen Storyboards laden, instantiieren Sie MyStoryboard. Wenn Sie das Standard-Storyboard verwenden, müssen Sie das System zwingen, Ihr spezielles Storyboard zu verwenden. Auch hier gibt es viele Möglichkeiten. Eine einfache Möglichkeit besteht darin, die Storyboard-Zugriffsmethode im Ansichtscontroller zu überschreiben.

Sie können MyStoryboard zu einer Proxy-Klasse machen, die alles an UIStoryboard weiterleitet, oder Sie können das Haupt-Storyboard isa-swizzle, oder Sie können Ihren lokalen Controller einfach einen von seiner Storyboard-Methode zurückgeben lassen.

Nun denken Sie daran, es gibt ein Problem hier. Was passiert, wenn Sie denselben View-Controller mehr als einmal auf den Stack schieben? Bei einem Cache wird das exakt gleiche View-Controller-Objekt mehrfach verwendet. Willst du das wirklich?

Wenn nicht, dann müssen Sie jetzt die Interaktion mit den Controller-Containern selbst verwalten, damit sie überprüfen können, ob dieser Controller ihnen bereits bekannt ist. In diesem Fall ist eine neue Instanz erforderlich.

So, ist eine Möglichkeit, zwischengespeicherte Controller zu erhalten, während Standard-Storyboard-Segmente (tatsächlich gibt es viele Möglichkeiten) ... aber das ist nicht unbedingt eine gute Sache, und sicherlich nicht, was Sie standardmäßig erhalten .

+0

Danke für die ausführliche Antwort. Es ist in Ordnung, die Controller nicht zwischenzuspeichern. Ich wollte nur den Lade-Entlade-Zyklus ein wenig mehr verstehen, so dass ich genau verstehen konnte, was ich in viewDidLoad sehen kann und was nicht. Es ist jetzt viel klarer. – Imran

+1

ViewWillUnload und ViewDidUnload sind eigentlich veraltet, Sie sollten also keinen Code mehr dort einfügen. Behandle didReceiveMemoryWarning, um Ressourcen freizugeben, wenn der Arbeitsspeicher unter Druck steht. –

+0

Ahh direkt in IOS6. Klingt so, als ob es keinen wirklichen Sinn mehr gibt, ViewLoad zu sehen, es könnte auch ein Konstruktor sein. Ich nehme an, dass Speicherwarnungen nicht mehr dazu führen, dass viewDidUnload aufgerufen wird? Diese Art ändert die Dinge, weil es heißt, ViewDidLoad wird nur einmal aufgerufen? Ich habe im Moment keine IOS6 zum Testen. – Imran

Verwandte Themen