2010-06-03 19 views
16

Cuando mi aplicación de iPhone recibe una advertencia de memoria, las vistas de UIViewControllers que no están actualmente visibles se descargan. En un controlador en particular, descargar la vista y las salidas es bastante fatal.UIViewController impide que la vista se descargue

Estoy buscando una forma de evitar que esta vista se descargue. Encuentro este comportamiento bastante estúpido, tengo un mecanismo de caché, así que cuando aparece una advertencia de memoria, descargo toneladas de datos y libero suficiente memoria, pero definitivamente necesito esta vista intacta.

Veo que UIViewController tiene un método unloadViewIfReloadable, que se invoca cuando aparece la advertencia de memoria. ¿Alguien sabe cómo decirle a Cocoa Touch que mi vista no es recargable?

¿Alguna otra sugerencia de cómo evitar que mi vista se descargue en la advertencia de memoria?

Gracias de antemano


documentos de Apple sobre el ciclo de vista de la vida de un controlador de vista dice:

didReceiveMemoryWarning - El valor predeterminado aplicación libera la vista única si se determina que es Es seguro hacer entonces

Ahora ... Anulo el didReceiveMemoryWarning con una función vacía que simplemente llama a NSLog para avisarme que se recibió una advertencia. Sin embargo, la vista se descarga de todos modos. Además, en qué criterios se decide exactamente si una vista es segura para descargar ... ¡oh! ¡Muchas preguntas!

+0

Quizás deba refaccionar su diseño para que las piezas que no se deben liberar sean parte de un objeto persistente separado, que no sea parte de la vista en sí. –

+0

Hola David, toda la jerarquía de vistas se muestra en la pantalla, y no quiero desmontarla y volver a compilarla, mientras que muestro un control de vista modal en la parte superior ... ¿no es una exageración? –

+2

Me encanta la solución provista por @umpo, pero chicos, este código da como resultado una advertencia de tiempo de ejecución como que la implementación 'MyViewController de -viewDidUnload hizo que la vista se volviera a cargar. Esto tendrá un impacto adverso en el rendimiento del sistema. - ¿Lo ignora o hace algo al respecto? – matm

Respuesta

13

Lo que parece estar funcionando para mí era para anular setView: para ignorar la configuración a cero. Es kludgy, pero luego, este es un tema kludgy, y esto hizo el truco:

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

wow ... eso es lo que llamo "pensar fuera de la caja", muchas gracias, cuando publique la próxima actualización de mis aplicaciones implementaré ese –

+1

solo un aviso - Implementé esto - 3 aplicaciones usándolo ahora en la App store , para mí la solución funciona bien –

+0

el problema con esto es que el método viewDidUnload todavía se llama, lo que puede confundir tu código si no tienes cuidado. Sospecho que es por eso que algunas personas están viendo las advertencias sobre el rendimiento adverso. –

1

¿Podría ser tan simple?

A pesar de que en ninguna parte de la documentación de esto se menciona, parece que si yo conservo mi punto de vista exclusivamente en viewDidLoad, entonces no ser liberado sobre Alerta de memoria. Intenté con varias advertencias consecutivas en el simulador y todas parecen buenas.

Entonces ... el truco por el momento es "retener" en viewDidLoad, y un lanzamiento en dealloc - de esta manera el viewcontroller está "atascado" con la vista hasta el momento en que necesita ser liberado.

voy a probar un poco más, y escribir sobre los resultados

+0

Este truco no parece funcionar para mí. Dealloc se llama directamente para que la vista se descargue independientemente. También intenté usar [self retain]; y eso lo hizo interminablemente tratar de tratarloc. No estoy seguro por qué. – jocull

15

De acuerdo con la documentación, la implementación predeterminada de didReceiveMemoryWarning: libera la vista si es seguro hacerlo (es decir: supervista == nil).

Para evitar que la vista sea liberado se podía anular didReceiveMemoryWarning: pero en su aplicación no llaman[super didReceiveMemoryWarning]. Ahí es donde la vista se libera por defecto (si no está visible).

El valor predeterminado doReceiveMemoryWarning libera la vista llamando al [viewcontroller setView:nil], por lo que puede anularla en su lugar.

+0

Reemplazar didReceiveMemoryWarning no me afectó. Sobreescribir setView hace que la consola de depuración se queje por completo sobre cómo afectará adversamente el rendimiento. ¿Esto es algo que te mantendrá fuera de la tienda de aplicaciones? – jocull

1

no creo que ninguna de estas ideas funcionan.Intenté sobreescribir [didReceiveMemoryWarning], y eso funcionó para algunos teléfonos, pero encontré que un teléfono descargó la vista ANTES de que se llamara siquiera el método (debe haber estado en la memoria extremadamente baja o algo así). Anular [setView] genera muchas advertencias de registro, así que no me arriesgaría por Apple. Mantener la vista solo filtrará esa vista; evitará bloqueos pero en realidad no funcionará; la vista se reemplazará la próxima vez que se cargue la interfaz de usuario de los controladores.

Así que realmente solo tienes que planear que tus vistas se descarguen cada vez que están fuera de la pantalla, lo que no es ideal, pero ahí tienes. Los mejores patrones que he encontrado para trabajar con esto son la confirmación inmediata para que su UI esté siempre actualizada, o copy-edit-copy, donde copie su modelo a una instancia temporal, llene sus vistas y use la confirmación inmediata con esa instancia, luego copie los cambios a su modelo original cuando el usuario presione "guardar" o lo que sea.

+1

para mí anulando el método setView: el método funciona perfectamente, no produce advertencias y ya tengo 3 aplicaciones en App Store usando esta técnica –

+0

Desafortunadamente eso no soluciona la pantalla blanca en blanco que saludará al usuario cuando se descarta una vista modal para revelar una vista vacía subyacente. – Oscar

1

Debido a que la solución aceptada tiene problemas con viewDidUnload que aún recibe una llamada a pesar de que se bloqueó la vista de borrar, estoy usando un enfoque diferente aunque frágil. El sistema descarga la vista usando un mensaje unloadViewForced: al controlador, así que estoy interceptando eso para bloquear el mensaje. Esto evita la llamada confusa al viewDidUnload. Aquí está el código:

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

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

Esto tiene evidentes problemas ya que está interceptando un mensaje de indocumentado en UIViewController.

progrmr publicó una respuesta arriba que recomienda interceptar didReceiveMemoryWarning en su lugar. Basado en los rastros de pila que he visto, interceptar eso también debería funcionar. Aunque no he probado esa ruta porque me preocupa que haya otra limpieza de la memoria que también estaría bloqueada (como hacer que no llame a los controladores de vista infantil con el mensaje de advertencia de memoria).

+0

¿Dónde especifica categoría para 'UIViewController'? En el archivo '.m'? Por alguna razón, mi sobrecargado 'unloadViewForced' nunca se llama. – expert

+0

Sí, en el archivo .m. –

+0

No he revisado para ver si dejó de funcionar con las actualizaciones recientes de iOS. Como es una API no documentada, podría haber desaparecido o cambiado de nombre. –

Cuestiones relacionadas