2010-06-03 17 views
16

Wenn meine iPhone-App eine Speicherwarnung erhält, werden die Ansichten von UIViewControllern, die momentan nicht sichtbar sind, entladen. In einer bestimmten Steuerung ist das Entladen der Sicht und der Ausgänge eher fatal.UIViewController verhindert, dass die Anzeige entladen wird

Ich bin auf der Suche nach einer Möglichkeit zu verhindern, dass diese Ansicht entladen wird. Ich finde dieses Verhalten ziemlich blöd - ich habe einen Cache-Mechanismus, also wenn eine Speicherwarnung kommt - ich entlade sehr viele Daten und ich habe genügend Speicher frei, aber ich brauche diese Ansicht definitiv nicht.

Ich sehe, UIViewController hat eine Methode unloadViewIfReloadable, die aufgerufen wird, wenn die Speicherwarnung kommt. Weiß jemand Cocoa Touch, dass meine Ansicht nicht nachladbar ist?

Haben Sie noch weitere Vorschläge, wie Sie verhindern können, dass meine Ansicht in der Speicherwarnung gelöscht wird?

Vielen Dank im Voraus


Apple-Dokumentation zu der Ansicht Lebenszyklus eines View-Controller sagt:

didReceiveMemoryWarning - Die Standard Implementierung nur die Sicht freigibt wenn es, dass es bestimmt ist sicher zu tun so

Jetzt ... Ich überschreibe die didReceiveMemoryWarning mit einer leeren Funktion, die nur NSLog anruft, um mir mitzuteilen, dass eine Warnung empfangen wurde. Allerdings - die Ansicht wird trotzdem gelöscht. Plus, nach welchen Kriterien genau entschieden wird, ob eine Ansicht sicher entladen werden kann ... oh! so viele Fragen!

+0

Vielleicht sollten Sie Ihr Design so umgestalten, dass die Teile, die nicht freigegeben werden müssen, Teil eines separaten permanenten Objekts sind, das nicht Teil der Ansicht selbst ist. –

+0

Hallo David, es gibt eine ganze Hierarchie von Ansichten auf dem Bildschirm, und ich möchte es nicht abreißen und dann wieder aufbauen, während ich oben einen modalen Viewcontroller zeige ... ist das kein Overkill? –

+2

Ich liebe die Lösung von @umpo, aber Jungs, dieser Code führt zu einer Laufzeitwarnung wie das 'MyViewController-Implementierung von -viewDidUnload verursacht die Ansicht neu geladen werden. Dies wird sich negativ auf die Systemleistung auswirken. - ignorieren Sie es einfach oder tun Sie etwas dagegen? – matm

Antwort

13

Was für mich zu arbeiten scheint, war zu überschreiben setView: zu ignorieren Einstellung auf Null. Es ist kludgy, aber dann ist dies ein kludgy Problem, und dies hat den Trick:

-(void)setView:(UIView*)view { 
    if(view != nil || self.okayToUnloadView) { 
     [super setView:view]; 
    } 
} 
+0

wow ... das nenne ich "über den Tellerrand hinaus" Danke eine Tonne, wenn ich nächstes Update meiner Apps rolle, werde ich das implementieren –

+1

nur einen Hinweis - ich habe das implementiert - 3 Apps nutzen es jetzt im App Store , für mich funktioniert die Lösung gut –

+0

das Problem damit ist, dass die ViewDidUnload -Methode immer noch aufgerufen wird, die Ihren Code verwirren können, wenn Sie nicht vorsichtig sind. Ich vermute, das ist der Grund, warum manche Leute die Warnungen über schlechte Leistungen sehen. –

1

Könnte es so einfach sein?

Obwohl nirgendwo in der Dokumentation dies erwähnt wird, scheint es, dass wenn ich ausschließlich meine Ansicht in ViewDidLoad behalten, es nicht auf Memory Warning freigegeben wird. Ich habe es mit mehreren aufeinander folgenden Warnungen im Simulator versucht und alle scheinen immer noch gut zu sein.

Also ... der Trick im Moment ist "behalten" in viewDidLoad, und eine Freigabe in dealloc - auf diese Weise ist der Viewcontroller mit der Ansicht bis zur Zeit fest, die es freigegeben werden muss.

Ich werde einige mehr testen, und über die Ergebnisse

+0

Dieser Trick schien nicht für mich zu arbeiten. Dealloc wird direkt aufgerufen, so dass die Ansicht unabhängig davon gelöscht wird. Ich habe auch versucht, [sich selbst zu behalten]; und das hat es einfach endlos versucht zu lösen. Nicht sicher warum. – jocull

15

Nach den Dokumenten, die Standardimplementierung von didReceiveMemoryWarning: gibt den Blick schreiben, wenn es sicher ist, zu tun (dh: Superview == null).

Um zu verhindern, dass die Ansicht freigegeben wird, könnten Sie didReceiveMemoryWarning überschreiben: aber in Ihrer Implementierung rufen Sie nicht[super didReceiveMemoryWarning]. Dort wird die Ansicht standardmäßig angezeigt (wenn nicht sichtbar).

Der Standardwert didReceiveMemoryWarning gibt die Ansicht durch Aufrufen von [viewcontroller setView:nil] frei, sodass Sie diese stattdessen überschreiben können.

+0

Das Überschreiben von didReceiveMemoryWarning hatte keinen Einfluss auf mich. Das Überschreiben von setView führt dazu, dass die Debug-Konsole sich über die Auswirkungen auf die Leistung beschweren muss. Ist das etwas, das dich aus dem App Store heraushält? – jocull

1

Ich glaube nicht, dass diese Ideen arbeiten.Ich habe versucht, [didReceiveMemoryWarning] zu überschreiben, und das funktionierte für einige Telefone, aber fand, dass ein Telefon die Ansicht löschte, BEVOR diese Methode sogar angerufen wurde (muss in extrem niedrigem Gedächtnis oder in etwas gewesen sein). Das Überschreiben von [setView] erzeugt eine Menge von Protokollwarnungen, so dass ich das nicht von Apple riskieren würde. Wenn Sie die Ansicht beibehalten, wird nur diese Ansicht verloren - sie verhindert zwar Abstürze, funktioniert aber nicht wirklich - die Ansicht wird beim nächsten Laden der Controller-Benutzeroberfläche ersetzt.

Also wirklich müssen Sie nur planen, dass Ihre Ansichten entladen werden, immer wenn sie außerhalb des Bildschirms sind, was nicht ideal ist, aber Sie gehen. Die besten Muster, die ich gefunden habe, um damit zu arbeiten, sind sofortiges Commit, so dass Ihre Benutzeroberfläche immer auf dem neuesten Stand ist, oder Kopieren-Bearbeiten-Kopieren, wo Sie Ihr Modell in eine temporäre Instanz kopieren, Ihre Ansichten befüllen und sofortiges Commit verwenden In diesem Fall kopieren Sie die Änderungen zurück in das ursprüngliche Modell, wenn der Benutzer auf "Speichern" oder was auch immer drückt.

+1

für mich überschreiben die setView: -Methode funktioniert perfekt, erzeugt keine Warnungen und ich habe bereits 3 Anwendungen im App Store mit dieser Technik –

+0

Leider tut dies nichts, um den leeren weißen Bildschirm zu adressieren, der den Benutzer begrüßt, wenn eine modale Ansicht abgewiesen wird um eine zugrunde liegende leere Sicht zu enthüllen. – Oscar

1

Da die akzeptierte Lösung Probleme mit viewDidUnload immer noch aufgerufen wird, obwohl die Sicht blockiert wurde von gelöscht werden, verwende ich eine andere, obwohl immer noch fragile Ansatz. Das System entlädt die Ansicht unter Verwendung einer unloadViewForced: Nachricht an die Steuerung, so dass ich abfange, um die Nachricht zu blockieren. Dies verhindert den verwirrenden Aufruf an viewDidUnload. Hier ist der Code:

@interface UIViewController (Private) 
- (void)unloadViewForced:(BOOL)forced; 
@end 

- (void)unloadViewForced:(BOOL)forced { 
    if (!_safeToUnloadView) { 
     return; 
    } 
    [super unloadViewForced:forced]; 
} 

Dies hat offensichtliche Probleme, da es eine nicht dokumentierte Nachricht in UIViewController abfängt.

progrmr hat eine Antwort geschrieben, die stattdessen das Abfangen von didReceiveMemoryWarning empfiehlt. Basierend auf den Stack-Spuren, die ich gesehen habe, sollte das auch funktionieren. Ich habe diese Route jedoch noch nicht ausprobiert, da ich befürchte, dass andere Speicherbereinigungen möglich sind, die ebenfalls blockiert werden (z. B. wenn die untergeordneten View-Controller nicht mit der Speicherwarnmeldung aufgerufen werden).

+0

Wo geben Sie die Kategorie für 'UIViewController' an? In '.m' Datei? Aus irgendeinem Grund wird mein überladenes 'unloadViewForced' niemals aufgerufen. – expert

+0

Ja, in der .m-Datei. –

+0

Ich habe nicht überprüft, ob es nicht mehr mit den neuesten iOS-Updates funktioniert. Da es sich um eine undokumentierte API handelt, könnte sie weggegangen sein oder umbenannt worden sein. –

Verwandte Themen