web-dev-qa-db-fra.com

Ordre d'initialisation et de chargement de UIViewController

Je suis assez nouveau dans la programmation de l'interface utilisateur sur Mac et iPhone, et j'ai rencontré quelque chose qui me laisse quelque peu perplexe.

Un UIViewController dispose de 3 méthodes qui impliquent son initialisation et sa vue:

  1. init (et méthodes similaires à init)
  2. loadView
  3. viewDidLoad (méthode déléguée)

Je m'attends à 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).

Ce n'est qu'une fois l'objet initialisé que je m'attends à 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 effectuer d'appels de délégué, car le 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 été exécutés:

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 avertissant le délégué au fur et à mesure, le code doit-il résider dans la méthode init? N'est-ce pas la raison pour laquelle loadView existe pour permettre à ce code d'être exécuté 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 [super init], est-ce vrai, ou est-ce que je m'y trompe?

Merci d'avance :)

37
Dani

Le système de chargement des vues sur l'iPhone fonctionne comme ceci:

Lorsque vous initialisez un contrôleur de vue (avec -init ou -initWithNibName: bundle :), il ne crée pas et n'initialise pas réellement la vue. Lorsque vous appelez -view pour la première fois, il appelle -loadView. Par défaut, -loadView charge simplement la vue à partir du fichier xib (nibName). Si vous remplacez cela, cependant, vous êtes responsable de la création de la vue et de son affectation à la propriété de vue du contrôleur de vue. Par 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, ce qui est différent de la vue qui devient visible et s'affiche à l'écran, elle 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 possède une sortie/ivar). Lorsque la vue est nulle, le nombre de retenues de cette vue est 1. Il n'est pas logique qu'une vue reste si une vue ne s'affiche pas, vous la définissez donc sur zéro dans -viewDidUnload (qui est un crochet pour chaque fois que la vue est définie sur néant).

30
Saurabh Sharan

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

Essayez de remplacer et de l'utiliser au lieu d'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];
16
gerry3
-(void)awakeFromNib
{
}

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

la séquence appropriée 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

Lors du passage à un nouveau ViewController

-(void)viewWillDisappear
-(void)viewDidDisappear

En revenant au premier ViewController

-(void)viewWillAppear
-(void)viewDidAppear
13
Sauvik Dolui

gerry3 a raison. Ce truc me dérange toujours aussi. Consultez les documents sur initialiseurs désignés .

Notez également que si votre contrôleur est créé par une nib en cours de chargement, seul initWithCoder sera appelé. loadView n'est pas appelé dans ce cas non plus.

À cause de cela, il semble que la plupart du code que j'ai vu effectue la plupart des initialisations dans des choses comme viewDidLoad même si cela semble incorrect, mais cela semble être la meilleure méthode qui est appelée dans les deux cas où quelque chose est chargé dans une nib et créé par programme.

Mais la raison pour laquelle cela semble hors de propos est que le [super init] appelle loadView etc. -

4
Nimrod

En prenant la suggestion de @ Nimrod, j'ai fait quelque chose comme:

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

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

afficher le cycle de vie http://developer.Apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/Art/loading_a_view_into_memory.jpg

Cela provient de: http://developer.Apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html#//Apple_ref/doc/uid/TP40007457-CH10-SW1

0
bitoiu