2010-01-13 14 views
35

Soy bastante nuevo en la programación de IU en la Mac y el iPhone, y me he topado con algo que me desconcierta un tanto.Orden de inicialización y carga de UIViewController

A UIViewController tiene 3 métodos que implican la inicialización de la misma y su vista:

  1. init (y métodos init-como)
  2. loadView
  3. viewDidLoad (método delegado)

Espero que ocurran en el orden anterior. El primer UIViewController es asignado por algún otro objeto, luego se llama inmediatamente a init (o a algún otro método init, como initWithStyle).

Solo una vez que se inicializa el objeto, espero que llame a su propia función loadView, después de lo cual la vista, una vez cargada, llama al método de delegado viewDidLoad.

Esto no sucede, por ejemplo:

@implementation UIViewControllerSubclass 

- (id)init { 
     NSLog(@"0"); 
    if (self = [super init]) { 
     NSLog(@"1"); 
    } 
    return self; 
} 

- (void)loadView { 
    [super loadView]; 
    NSLog(@"2"); 
} 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    NSLog(@"3"); 
} 

@end 

produce la salida de la consola:

0 
2 
3 
1 

Los métodos loadView y viewDidLoad, por lo tanto, no se pueden realizar llamadas de delegados, como el delegado es por lo general configurado después de la llamada a [superinicial], que (como se muestra arriba) se llama después de que loadView y viewDidLoad se hayan ejecutado:

UIViewControllerSubClass *someViewController = [[UIViewControllerSubclass alloc] init]; 
[viewController setDelegate:self]; 

Si quiero ejecutar el código que configura el ViewController de alguna manera, notificando al delegado como va, ¿debería residir el código en el método init? ¿No es la razón por la que loadView existing permite que dicho código se ejecute en el momento apropiado?

Se Me parece que voy a tener que crear un nuevo método initWithDelegate que establece el Ivar delegado antes de llamar [súper init], es este derecho, o voy de este por el camino equivocado?

Gracias de antemano :)

Respuesta

30

El sistema de carga de la vista en el iPhone funciona así:

Al inicializar un controlador de vista (ya sea con -init o -initWithNibName: bundle :), en realidad no crea e inicializa la vista. Cuando llama -view por primera vez, llama -loadView. De forma predeterminada, -loadView solo carga la vista desde el archivo xib (nibName). Sin embargo, si anulas esto, eres responsable de crear la vista y asignarla a la propiedad de vista del controlador de vista. A modo de ejemplo:

- (void)loadView 
{ 
    UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; 
    // add subviews 
    self.view = view; 
    [view release]; 
} 

Cada vez que se crea la vista, que es diferente de la vista se hace visible y mostrar en pantalla, llama -viewDidLoad. (-viewDidAppear/-viewDidDisappear es para la visibilidad de la vista en la pantalla)

Como ya estamos fuera de la ruta, consideremos la gestión de la memoria. Cuando la vista está fuera de la pantalla, el sistema establecerá automáticamente la propiedad de vista de un controlador de vista en cero. El problema es que todas las subvistas de esa vista tienen filtraciones. ¿Cómo es eso? Bueno, el recuento de retención para cada subvista es 2 (las vistas retienen las subvistas, y su controlador de vista tiene una salida/ivar). Cuando la vista es nula, el recuento retenido de esa vista es 1. No tiene sentido que una vista se quede si no se muestra una vista, por lo que la configura en nil en -viewDidUnload (que es un gancho para cada vez que la vista se establece en nil).

+2

Nota: viewDidUnload está en desuso a partir de iOS 6.0. – Brainware

16

El método initWithNibName:bundle: es el inicializador designado para la clase UIViewController.

primordial Try y usarlo en lugar de init:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 
    } 
    return self; 
} 

...

UIViewControllerSubClass *someViewController = [[UIViewControllerSubclass alloc] initWithNibName:@"UIViewControllerSubclass" bundle:nil]; 
+1

Hm .. Cuando trato de ejecutarlo en 5.1 Simulator, el depurador no está llegando a '- [MyViewController initWithNibName: bundle:]' o '- [MyViewController init]' –

+0

aquí es donde inicia el UIViewController, como tal, pero ** no ** donde engaña con la vista. – Fattie

4

gerry3 es el adecuado. Esto también me confunde a mí también. Consulte los documentos en designated initializers.

También tenga en cuenta que si su controlador se crea con una plumilla que se está cargando, solo se llamará a initWithCoder. loadView no se llama tampoco en ese caso.

Debido a esto, parece que la mayor parte del código que he visto hace más inicialización en cosas como viewDidLoad aunque parezca incorrecto, pero parece ser el mejor método que se llama en ambos casos donde se carga algo una semilla y creado mediante programación.

Pero la razón de que esto parece fuera de orden es que el [súper init] está llamando loadView etc. -

+0

"la mayor parte del código que he visto hace la mayoría de las inicializaciones en cosas como viewDidLoad, aunque eso parece incorrecto" En realidad, está equivocado. La razón de esto es que su vista podría estar descargada mientras su controlador de visualización todavía estaría disponible, listo para volver a cargar la vista bajo demanda. Por lo tanto, corre el riesgo de volver a inicializar sus variables y, en algunos casos, eso puede ocasionar problemas lógicos difíciles de seguir en su aplicación. – KPM

0

Tomando @ sugerencia de Nimrod que hice algo como:

-(void)viewDidLoad 
{ 
    // Init code here 
} 

No sé si esto puede causar problemas con una fuga de memoria pero mirando documentos de Apple no parece crear cualquier ciclo:

view lifecycle http://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/Art/loading_a_view_into_memory.jpg

Esto fue tomada de: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html#//apple_ref/doc/uid/TP40007457-CH10-SW1

12
-(void)awakeFromNib 
{ 
} 

solo se invoca si está usando storyboard para almacenar el ViewController dibujado en el storyboard Nib --- significa paquete de interfaz.

la secuencia correcta es

-(void)initWithCoder 
-(void)awakefromNib //(if story board is used) 
    or 
-(void)loadView----() //if manually generating the view contoller 

-(void)viewDidLoad-----(called only once in the life cycle of viewController) 
-(void)viewWillAppear 
-(void)viewDidAppear 

Mientras se mueve a una nueva ViewController

-(void)viewWillDisappear 
-(void)viewDidDisappear 

Al regresar a la primera ViewController

-(void)viewWillAppear 
-(void)viewDidAppear 
+0

loadView también se llama si se usa un storyboard – malhal

Cuestiones relacionadas