2009-01-29 8 views
5

Hintergrund:iPhone Entwicklung - Simulieren Speicher Warnung

ich eine Tab-Leiste Anwendung haben. Jede Registerkarte enthält einen Navigationscontroller, der es dem Benutzer ermöglicht, von einer Ansicht zur anderen zu wechseln und eine Drilldown-Information der Daten zu zeigen (jede Ansicht wird von einem Ansichtscontroller gehandhabt und jede Sichtkontrollklasse hat die Methode didReceiveMemoryWarning). Listen werden ausgefüllt, indem die Daten aus den Webdiensten abgerufen werden.

Problem:

Wenn i „Hardware> Speicher Warnung Simulieren“ verwenden Option von iPhone Simulator, die didReceiveMemoryWarning Methode ist für alle meine Ansicht-Controller genannt - auch die, die der Benutzer sieht. Ich möchte keinen Inhalt löschen, der vom aktiven Ansichtscontroller verwendet wird. Wie kann ich das erreichen?

Welche Methode sollte die Implementierung haben, um die Daten neu zu laden, nachdem die Daten wegen Speicherwarnung freigegeben wurden? (Ich sehe, dass die View-Controller-Klassen, die eine Tabellenansicht Aufruf viewDidLoad Methode enthalten, wenn der Benutzer zu dieser Ansicht kommt zurück, aber wenn die Ansicht enthält (etwa UIWebView) dann viewDidLoad Methode nicht aufgerufen wird. Warum ist das so?)

bearbeitet (freitag, 30. Januar 2009 - 15.10 Uhr)

. (Anmerkung: ich bin für die Erstellung von Ansichten Interface Builder verwenden und loadView Methode kommentiert out)

Also, wenn ein view-Controller empfängt eine Speicher-Warnmeldung, dies sind die Schritte, die ausgeführt werden:

  1. folgende Verfahren genannt:

    - (void)didReceiveMemoryWarning { 
        [super didReceiveMemoryWarning]; 
    } 
    
  2. Als Ergebnis des Aufrufs [super didReceiveMemoryWarning], wird [self setView:nil] automatisch aufgerufen?

  3. Wenn Ressourcen gelöscht werden sollen, sollte die Methode setView überschrieben werden, um die lokalen Ressourcen zu löschen.

  4. [self setView:nil] wird nicht aufgerufen, wenn die Ansicht gerade aktiv ist (Standardeinstellung). Recht? - Ich bin wirklich gespannt, welche Methode diese Entscheidung trifft und wie?

Können Sie bitte bestätigen. Plus, ich bekam einen Fehler nach diesem Ansatz, aber myObject = nil nach der Freigabe myObject in dealloc Methode der Controller-Klasse hinzugefügt behoben das Problem. Vielen Dank.

Antwort

12

Dies ist eine alte Frage, aber ich habe nicht eine richtige Antwort sehen, so hier geht:

Wenn eine Speicherwarnung empfangen wird, wird -didReceiveMemoryWarning in alle View-Controller genannt, ob sie die „aktuelle“ sind eins oder nicht. Die View-Controller hören einfach auf die Speicherwarnungsereignisübertragung.

Wenn die View-Controller-Ansicht zum Zeitpunkt der Speicherwarnung nicht verwendet wird, wird sie vom Controller entladen, indem die Eigenschaft auf null gesetzt wird. Woher weiß es, ob die Ansicht verwendet wird? Durch die Ansicht -superview Eigenschaft. Wenn view.superview Null ist, ist die Ansicht kein Teil eines Baums und kann sicher entladen werden.

Sobald dies passiert, wird der Controller -viewDidUnload aufgerufen. Dies ist der richtige Ort, um alle Steckdosen und alles, was in -viewDidLoad neu erstellt wird, zu entladen.


Was ist -didReceiveMemoryWarning für? Ihr Controller verfügt möglicherweise über Objekte, die erst beim Zugriff instanziert werden. Zum Beispiel könnten Sie einen Controller haben, der manchmal einen großen Teil der Daten aus einer Datei benötigt, aber nicht immer. Sie könnten eine Eigenschaft für das so eingestellt haben:

- (NSData*)bigChunkOfData { 
    // Get data from our instance variable _data, read from disk if necessary 
    if (_data == nil) { 
    _data = [[NSData alloc] initWithContentsOfFile:@"/path/to/data"]; 
    } 
    return _data; 
} 

Dadurch werden die Daten von der Festplatte dieses ersten Mal gelesen, es dann in einer Instanzvariablen halten. Da die Variable _data auf Anforderung erstellt wird, ist es für uns sicher, sie in Situationen mit wenig Speicher zu entladen: Sie wird beim nächsten Mal wieder erstellt.

- (void)didReceiveMemoryWarning { 
    [super didReceiveMemoryWarning]; 

    [_data release]; 
    _data = nil; // <-- Very important: don't leave strong references dangling. 
} 
8

ich meine sauber wie auf den Punkt:

-(void)setView:(UIView*)view 
{ 
    [super setView:view]; 
    if(view == nil) 
    { 
     // Our view has been cleared, therefore we should clean up everything 
     // we are not currently using 
.... 

setView:nil wird von UIViewController in Antwort auf eine Speicher Warnung genannt, wenn diese Ansicht derzeit nicht sichtbar ist - das ist im Grunde das, was Sie wissen wollen.

EDITED

In Antwort auf die Follow-ups:

  1. Richtig.
  2. Das ist, was ich tue, und es funktioniert für mich.
  3. Korrekt. Die Implementierung von didReceiveMemoryWarning in UIViewController ist was das tut. Wenn Sie nicht didReceiveMemoryWarning außer Kraft setzen, dann ist die Basisklassenimplementierung in UIViewController aufgerufen wird - wenn Sie es tun außer Kraft setzen, natürlich sollten Sie rufen:

    [super didReceiveMemoryWarning] 
    
+0

Auch wenn ich didReceiveMemoryWarning Methode nicht außer Kraft setzen, ist meine Ansicht gelöscht. Warum das? – Mustafa

+1

Kein Überschreiben bedeutet, dass Sie das von UIViewController implementierte Standardverhalten erhalten, nämlich Ihre Sicht zu löschen. Wenn Ihre Überschreibung einfach die Super-Implementierung aufruft, ist das das gleiche wie überhaupt keine Überschreibung. –

1

Hinsichtlich der effizienteren Verwaltung und Speicher Warnungen:

UIKit erlaubt nicht nur die Navigation zurück von einem View-Controller, sondern ermöglicht auch die Navigation zu anderen View-Controllern von bestehenden. In diesem Fall wird ein neuer UIViewController zugewiesen und dann in den View geladen. Der alte Ansichts-Controller wird nicht mehr angezeigt und wird inaktiv, besitzt aber immer noch viele Objekte - einige in benutzerdefinierten Eigenschaften und Variablen und andere in der Ansichtseigenschaft/-hierarchie. Und das tut auch der neue visual view controller, in Bezug auf seine View-Objekte.

Aufgrund der begrenzten Speicherkapazität mobiler Geräte ist es möglicherweise zu umfangreich, die beiden Objektgruppen zu besitzen, eine im Offscreen-View-Controller und eine andere im Onscreen-View-Controller. Wenn UIKit es für notwendig erachtet, kann es einen Teil des Speichers des Offscreen-View-Controllers wiederherstellen, der sowieso nicht angezeigt wird; UIKit weiß, welcher View-Controller auf dem Bildschirm und welcher außerhalb des Bildschirms ist, denn schließlich ist es derjenige, der sie verwaltet (wenn Sie presentModalViewController:animated: oder dismissModalViewControllerAnimated: aufrufen). Jedes Mal, wenn es sich unter Druck gesetzt hat, generiert UIKit eine Speicherwarnung, die Ihre Offscreen-Ansicht aus der Ansichtshierarchie entfernt und wieder freigibt. Rufen Sie dann Ihre benutzerdefinierte viewDidUnload-Methode auf, um das Gleiche für Ihre Eigenschaften und Variablen zu tun. UIKit gibt self.view automatisch frei und erlaubt uns, unsere Variablen und Eigenschaften in unserem viewDidUnload Code manuell freizugeben. Dies gilt für alle Offscreen-View-Controller.

Wenn das System nicht mehr genügend Arbeitsspeicher hat, wird didReceiveMemoryWarning ausgelöst. Offscreen-Ansichten werden zurückgewonnen und nach Speicherwarnung freigegeben, aber Ihre Bildschirmansicht wird nicht freigegeben - sie ist sichtbar und wird benötigt. Wenn Ihre Klasse viel Arbeitsspeicher besitzt, wie z. B. Caches, Bilder oder Ähnliches, sollten Sie sie auch dann löschen, wenn sie auf dem Bildschirm angezeigt werden: didReceiveMemoryWarning. Andernfalls wird Ihre App möglicherweise für die Überbrückung von Systemressourcen beendet. Sie müssen diese Methode überschreiben, um sicherzustellen, dass Sie Ihren Speicher bereinigen; Erinnern Sie sich daran, dass Sie [super didReceiveMemoryWarning]; anrufen.

Eine noch aufwändigere Erklärung hier verfügbar: http://myok12.wordpress.com/2010/11/30/custom-uiviewcontrollers-their-views-and-their-memory-management/

0

Glücklicherweise hat der Simulator eine praktische Funktion, die Sie wenig Speicher auf den Prüfstand stellen ermöglicht. Legen Sie einige NSLog() Aussagen in beiden viewDidLoad und didReceiveMemoryWarning, wie folgt aus: 

- (void)viewDidLoad { 
    NSLog(@"viewDidLoad"); 
    ... 
} 

- (void)didReceiveMemoryWarning { 
    NSLog(@"didReceiveMemoryWarning"); 
}