2010-01-13 2 views
35

Je suis assez novice en matière de programmation UI sur Mac et iPhone, et j'ai croisé quelque chose qui me laisse perplexe.L'ordre de l'initialisation et du chargement de UIViewController

A UIViewController a 3 méthodes qui impliquent l'initialisation de celui-ci et son point de vue:

  1. init (et les méthodes init-analogues)
  2. loadview
  3. viewDidLoad (méthode déléguée)

Je m'attendrais à ce que cela se produise dans l'ordre ci-dessus. Le premier UIViewController est alloué par un autre objet, puis init est immédiatement appelé (ou une autre méthode init, comme initWithStyle).

Une fois l'objet initialisé, je m'attendrais à ce qu'il appelle sa propre fonction loadView, après quoi la vue, une fois chargée, appelle la méthode déléguée viewDidLoad.

Cela ne se produit pas, par exemple:

@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 

produit la sortie de la console:

0 
2 
3 
1 

Les méthodes loadview et viewDidLoad, par conséquent, ne peuvent pas faire des appels de délégué, en tant que délégué est généralement défini après l'appel à [super init], qui (comme indiqué ci-dessus) est appelé après loadView et viewDidLoad ont exécuté:

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

Si je veux exécuter du code qui configure le ViewController d'une manière ou d'une autre, en notifiant le délégué comme il se passe, le code devrait-il résider dans la méthode init? La raison pour laquelle loadView existe n'est-elle pas d'autoriser l'exécution de ce code au moment approprié?

Il me semble que je vais devoir créer une nouvelle méthode initWithDelegate qui définit le délégué Ivar avant appeler, est ce droit, ou je vais sur ce dans le mauvais sens [super init]?

Merci à l'avance :)

Répondre

30

La vue système de chargement sur l'iPhone fonctionne comme ceci:

Lorsque vous initialisez un contrôleur de vue (soit avec -init ou -initWithNibName: bundle :), il ne crée et n'initialise pas la vue. Lorsque vous appelez -view pour la première fois, il appelle -loadView. Par défaut, -loadload ne charge que la vue du fichier xib (nibName). Si vous remplacez cela, vous êtes responsable de la création de la vue et de son affectation à la propriété view du contrôleur de vue. A titre d'exemple:

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

Chaque fois que vous créez la vue, qui est différent du point de vue de devenir visible et montrant à l'écran, il appelle -viewDidLoad. (-viewDidAppear/-viewDidDisappear est pour la visibilité de la vue à l'écran)

Puisque nous sommes déjà hors-piste, considérons la gestion de la mémoire. Lorsque la vue est hors écran, le système définit automatiquement la propriété de vue d'un contrôleur de vue sur zéro. Le problème est que toutes les sous-vues de cette vue fuient. Comment? Eh bien, le nombre de retenues pour chaque sous-vue est de 2 (les vues conservent les sous-vues, et votre contrôleur de vue a une sortie/ivar). Lorsque la vue est nulle, le nombre de retenues de cette vue est de 1. Cela n'a pas de sens pour une vue de rester si une vue n'est pas affichée, donc vous le définissez à nil dans -viewDidUnload (qui est un crochet pour chaque fois que la vue est définie sur zéro).

+2

Remarque: viewDidUnload est obsolète depuis iOS 6.0. – Brainware

16

La méthode initWithNibName:bundle: est l'initialiseur désigné pour la classe UIViewController.

Essayez primordial et l'utiliser au lieu 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 .. Lorsque je tente de le lancer dans 5.1 Simulateur, le débogueur n'atteint pas '' - [MyViewController initWithNibName: bundle:] 'ou' - [MyViewController init] ' –

+0

c'est ici que vous initialisez le UIViewController, en tant que tel, mais ** not ** où vous tromper avec la vue. – Fattie

4

gerry3 est juste. Ce truc me trouble encore aussi. Découvrez les documents sur designated initializers.

Notez également que si votre contrôleur est créé par une plume en cours de chargement, seul initWithCoder sera appelé. loadView n'est pas appelé dans ce cas non plus. Pour cette raison, il semble que la plus grande partie du code que j'ai vu est la plus utilisée dans viewDidLoad, même si cela semble faux, mais il semble que ce soit la meilleure méthode qui soit appelée dans les deux cas. une plume et créé par programme.

Mais la raison pour laquelle cela semble hors d'usage est que le [super init] appelle loadview etc. -

+0

"La plus grande partie du code que j'ai vu fait le plus d'initialisation dans des choses comme viewDidLoad même si cela semble faux" En fait, il est faux. La raison en est que votre vue pourrait être déchargée pendant que votre viewcontroller serait toujours là, prêt à charger à nouveau la vue à la demande. Vous risquez donc de réinitialiser vos variables et, dans certains cas, cela pourrait entraîner des problèmes logiques difficiles à suivre dans votre application. – KPM

0

Prendre @ suggestion de Nimrod je l'ai fait quelque chose comme:

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

Je ne sais pas si cela peut causer des problèmes avec une fuite de mémoire mais en regardant les documents d'Apple, il ne semble pas créer un cycle:

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

Cela a été pris de: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html#//apple_ref/doc/uid/TP40007457-CH10-SW1

12
-(void)awakeFromNib 
{ 
} 

n'est appelé que si vous utilisez storyboard pour stocker le ViewController dessiné dans le story-board Nib --- signifie ensemble d'interface.

la séquence correcte est

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

Tout en se déplaçant à une nouvelle ViewController

-(void)viewWillDisappear 
-(void)viewDidDisappear 

En revenant à la première ViewController

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

loadView est également appelé si un story-board est utilisé – malhal

Questions connexes