Desarrollo de iPhone: simule la advertencia de memoria

Antecedentes :

Tengo una aplicación de barra de tabs. Cada pestaña contiene un controlador de navegación que permite al usuario hacer la transición de una vista a la otra mostrando una información didReceiveMemoryWarning de los datos (cada vista está siendo manejada por un controlador de vista y cada clase de controlador de vista tiene el método didReceiveMemoryWarning ). Las listas se completan extrayendo los datos de los servicios web.

Problema :

Cuando uso la opción “Hardware> Simular memoria de memoria” de iPhone Simulator, se didReceiveMemoryWarning método didReceiveMemoryWarning para TODOS mis controles de vista, incluso el que el usuario está viendo. No quiero borrar ningún contenido que esté siendo utilizado por el controlador de vista activo. ¿Cómo puedo lograr eso?

¿Qué método debería tener la implementación para volver a cargar los datos después de que se liberaran los datos debido a una advertencia de memoria? (Veo que las clases de controlador de vista que contienen una vista de tabla llaman viewDidLoad método viewDidLoad cuando el usuario regresa a esa vista, pero si la vista contiene (digamos UIWebView), entonces no se llama al método viewDidLoad . ¿Por qué?)

Editado (viernes 30 enero 2009 – 03:10 PM)

(Nota: estoy usando el constructor de Interfaces para crear vistas, y el método loadView está comentado).

Entonces, cuando un controlador de vista recibe un mensaje de advertencia de memoria, estos son los pasos que se llevan a cabo:

  1. El siguiente método se llama:

     - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } 
  2. Como resultado de la llamada a [super didReceiveMemoryWarning] , [self setView:nil] se llama automáticamente?

  3. Si se eliminan los recursos, se debe sobrescribir el método setView para borrar los recursos locales.

  4. [self setView:nil] no se llama si la vista está actualmente activa (Por defecto). ¿Derecha? – Estoy realmente curioso qué método toma esta decisión y cómo?

¿Puedes confirmar? Además, estaba obteniendo un error al seguir este enfoque, pero al agregar myObject = nil después de liberar dealloc en el método dealloc de la clase de controlador solucioné el problema. Gracias.

Esta es una vieja pregunta, pero no veo una respuesta adecuada, así que aquí va:

Cuando se recibe una advertencia de memoria, se llama a -didReceiveMemoryWarning en TODOS los controladores de vista, ya sean los “actuales” o no. Los controladores de vista simplemente están escuchando la transmisión de eventos de advertencia de memoria.

Si la vista del controlador de vista no se está utilizando en el momento de la advertencia de memoria, el controlador la descargará estableciendo la propiedad en cero. ¿Cómo sabe si se usa la vista? Por la propiedad de la vista de la vista. Si view.superview es nulo, la vista no es parte de ningún árbol y puede descargarse de manera segura.

Una vez que eso ocurre, se llama al controlador -viewDidUnload . Este es el lugar correcto para descargar cualquier punto de venta y todo lo que se volverá a crear en -viewDidLoad .


Entonces, ¿para qué es -didReceiveMemoryWarning ? Su controlador puede tener objetos que no se instancian hasta que se acceda. Por ejemplo, podría tener un controlador que a veces necesita una gran cantidad de datos de un archivo, pero no siempre. Podrías tener una propiedad configurada así:

 - (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; } 

Esto leerá los datos del disco esta primera vez, luego guárdelos en una variable de instancia. Como la variable _data se crea bajo demanda, es seguro para nosotros descargarla en situaciones de poca memoria: se creará nuevamente la próxima vez que la necesitemos.

 - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; [_data release]; _data = nil; // <-- Very important: don't leave strong references dangling. } 

Hago mi limpieza de esta manera:

 -(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 es llamado por UIViewController en respuesta a una advertencia de memoria, si esa vista no está actualmente visible, que es básicamente lo que quiere saber.

EDITADO

En respuesta a los seguimientos:

  1. Correcto.
  2. Eso es lo que hago, y funciona para mí.
  3. Correcto. La implementación de didReceiveMemoryWarning en UIViewController es lo que hace esto. Si no anula didReceiveMemoryWarning , se didReceiveMemoryWarning la implementación de la clase base en UIViewController; si lo reemplaza, obviamente debe llamar a:

     [super didReceiveMemoryWarning] 

Para asegurarme de que no tengo que manejar esto para cada viewcontroller que escribo … Acabo de crear una plantilla Xcode ViewController que proporciona pautas sobre qué objetos lanzar y cuándo …

más explicación aquí http://iphone2020.wordpress.com/2010/05/30/efficient-memory-handling-in-uiviewcontroller-part-1/

Espero que encuentre útil.

Con respecto a las advertencias de administración y memoria de vista:

UIKit no solo permite la navegación desde un controlador de vista, sino que también permite la navegación a otros controladores de vista desde los existentes. En tal caso, se asignará un nuevo UIViewController y luego se cargará en la vista. El controlador de vista anterior saldrá de la pantalla y se volverá inactivo, pero aún posee muchos objetos, algunos en propiedades y variables personalizadas y otros en la propiedad / jerarquía de la vista. Y también lo hace el nuevo controlador de vista visible, con respecto a sus objetos de vista.

Debido a la cantidad limitada de memoria de los dispositivos móviles, tener dos juegos de objetos, uno en el controlador de visualización fuera de pantalla y otro en el controlador de visualización en pantalla, puede ser demasiado para manejar. Si UIKit lo considera necesario, puede reclamar parte de la memoria del controlador de vista fuera de pantalla, que de todos modos no se muestra; UIKit sabe qué controlador de visualización está en la pantalla y cuál está fuera de la pantalla, ya que, después de todo, es el que los está administrando (cuando llama a presentModalViewController:animated: o dismissModalViewControllerAnimated: . Entonces, cada vez que se siente presionado, UIKit genera una advertencia de memoria, que descarga y libera su vista fuera de la pantalla desde la jerarquía de vista, luego llama a su método viewDidUnload personalizado para que haga lo mismo con sus propiedades y variables. UIKit lanza self.view automáticamente, permitiéndonos luego liberar manualmente nuestras variables y propiedades en nuestro código viewDidUnload. Lo hace para todos los controladores de vista fuera de pantalla.

Cuando el sistema se está quedando sin memoria, dispara un didReceiveMemoryWarning . Las vistas fuera de la pantalla serán reclamadas y liberadas al recibir una advertencia de memoria, pero su vista en pantalla no se liberará; es visible y necesaria. En caso de que su clase tenga mucha memoria, como cachés, imágenes o similares, didReceiveMemoryWarning es donde debe purgarlos, incluso si están en pantalla; de lo contrario, su aplicación podría ser cancelada por exceso de recursos del sistema. Debe anular este método para asegurarse de que limpia su memoria; solo recuerda que llamas a [super didReceiveMemoryWarning]; .

Una explicación aún más elaborada está disponible aquí: http://myok12.wordpress.com/2010/11/30/custom-uiviewcontrollers-their-views-and-their-memory-management/

Afortunadamente, el simulador tiene una práctica función que le permite poner a prueba situaciones de poca memoria. Ponga algunas declaraciones NSLog () en viewDidLoad y didReceiveMemoryWarning, así: 

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