web-dev-qa-db-fra.com

Quelle est la relation entre AppDelegate, RootViewController et UIApplication?

J'essaie de comprendre la relation entre appdelegate, RootViewControoler et UIApplication. Voici ce que j'ai un peu compris jusqu'ici:

Au démarrage de votre application, main.m est chargé.

À partir de là, votre MainWindow.xib est chargé.

Dans votre fichier MainWindow.xib, le propriétaire de votre fichier est de type UIApplication.

Vous définissez le délégué de votre UIApplication sur votre AppDelegate.

Dans le code source de votre AppDelegate, vous pouvez configurer votre RootViewController pour qu'il soit la première vue affichée.

Est-ce correct? Qu'est-ce qui incite AppDelegate à exécuter initialement

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { }

méthode?

25
guy8214

Lorsqu'une application Objective-C démarre, elle commence par exécuter la fonction nommée main (). Il ne doit pas obligatoirement figurer dans le fichier "main.m", mais c'est ainsi que l'assistant Xcode configure les choses.

Dans la fonction main () produite par l'assistant, il y a cette ligne:

int retVal = UIApplicationMain(argc, argv, nil, nil);

C’est ce qui lance le cadre "UIKit" qui constitue l’ensemble de l’application. Dans UIApplicationMain, un objet de type UIApplication est créé. Et une partie de ce que UIApplication fait au démarrage de l'application consiste à appeler la méthode applicationDidFinishLaunchingWithOptions sur le membre délégué de la classe UIApplication. Ce délégué est configuré dans le fichier MainWindow.xib pour être une instance de votre classe ProjectAppDelegate, une sous-classe de NSObject conforme au protocole UIApplicationDelegate.

Ce qui incite AppDelegate à exécuter initialement C'est ...

Parce que dans votre fichier MainWindow.xib, vous avez connecté (bien que l’assistant de projet ait effectivement établi la connexion) le propriétaire du fichier (qui est l’objet UIApplication) du "délégué" de celui-ci à l’objet UIApplicationDelegate du fichier .xib de UIApplicationDelegate est défini sur la sous-classe UIApplicationDelegate de votre application.

Et il n'y a rien de magique dans "MainWindow.xib", cela pourrait s'appeler "Foo.xib", l'important est que la propriété de votre fichier Info.plist appelée "Nom de base du fichier nib principal" soit "MainWindow". Essayez de renommer MainWindow.xib en Foo.xib et de changer le "Nom de base du fichier nib principal" dans votre Info.plist en "Foo" et vous verrez que cela fonctionne toujours.

EDIT: plus sur RootController

Encore une fois, le soi-disant "RootController" n'a rien de magique. Ceci n'est que le nom de la sous-classe UIViewController créée pour vous par l'assistant de création de projet Xcode.

L'assistant place du code dans le projet pour deux classes: ProjectAppDelegate et ProjectViewController. La classe ProjectAppDelegate contient deux membres de sortie:

IBOutlet UIWindow *window;
IBOutlet ProjectViewController *viewController;

dans le fichier MainWindow.xib, les instances de UIWindow et de ProjectViewController sont placées et connectées aux prises ci-dessus dans ProjectAppDelegate.

Ce qui fait apparaître votre contenu à l'écran, c'est ce code dans votre classe ProjectAppDelegate:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    // Override point for customization after application launch.

    // Add the view controller's view to the window and display.
    [self.window addSubview:viewController.view];
    [self.window makeKeyAndVisible];

    return YES;
}

Encore une fois, rien n’est vraiment magique à ce sujet: l’assistant de projet a créé un code qui ajoute la vue de votre ViewController "racine" à la vue de la fenêtre et la rend visible. Votre contrôleur de vue "racine" a été créé dans le fichier .xib et connecté à la sortie ProjectAppDelegate. 

Il est très instructif d'essayer de créer une application entièrement par vous-même sans utiliser aucun des fichiers de l'assistant. Vous en apprendrez beaucoup sur le fonctionnement des fichiers .xib et sur leur relation avec les objets de code.

44
Bogatyr

Le point de départ des applications iOS est toujours la fonction main() (merci @bogatyr) qui contient généralement un code similaire à,

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

Les deux derniers paramètres de UIApplicationMain sont importants et spécifient le nom de la classe principale et le délégué de l'application. Si elles sont nil, alors Info.plist sera recherché pour la fenêtre principale xib (généralement MainWindow.xib). 

// If nil is specified for principalClassName, the value for NSPrincipalClass
// from the Info.plist is used. If there is no NSPrincipalClass key specified, the
// UIApplication class is used. The delegate class will be instantiated 
// using init.
.. UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName);

Il n'est pas nécessaire de définir le propriétaire du fichier via xib, et ils peuvent être spécifiés directement dans cette fonction UIApplicationMain.

principalClassName peut être la chaîne UIApplication ou une sous-classe de UIApplication. De même, delegateClassName peut être directement spécifié dans cette méthode. La classe déléguée est instanciée en utilisant init comme le dit la documentation. Supposons que nous spécifions notre classe de délégué - MyAppDelegate en tant que chaîne,

UIApplicationMain(int argc, char *argv[], nil, @"MyAppDelegate");

Une instance d'UIApplication est d'abord instanciée, ce qui créera ensuite la classe déléguée à partir de cette chaîne à l'aide de NSClassFromString je suppose.

Une fois que delegateObject a été instancié et que l'application est prête, cet objet delegateObject sera informé à l'aide de la méthode delegate, didFinishLaunchingWithOptions.

Class delegateClass = NSClassFromString(@"MyAppDelegate");
id <UIApplicationDelegate> delegateObject = [[delegateClass alloc] init];

// load whatever else is needed, then launch the app
// once everything is done, call the delegate object to
// notify app is launched
[delegateObject application:self didFinishLaunchingWithOptions:...];

C’est ainsi que UIApplication le traiterait par programme si aucun nib n’est utilisé. Utiliser une plume au milieu n’est pas très différent.

15
Anurag

MainWindow.xib est défini dans votre info.plist en tant queMain nib file base name. Dans votre MainWindow.xib, vous définissez le premier contrôleur que vous souhaitez charger, dans votre cas, RootViewController.

didFinishLaunchingWithOptions: fait partie du protocole UIApplicationDelegate. Cette méthode (sous iOS4.0 +) est toujours connue pour être la première à être appelée lors du lancement d'une application.

1
WrightsCS

Puisque votre AppDelegate est un délégué de UIApplication, il écoute toutes les notifications que la classe UIApplication publie pendant son cycle de vie. La notification didFinishLaunching est l'une d'elles et votre AppDelegate appelle la méthode susmentionnée.

1
Eimantas

Pour les applications Universal - iPhone + iPad -, vous pouvez spécifier que différentes cartes NIB doivent être chargées sur chaque plate-forme, soit dans le panneau d'informations cible, soit en ajoutant les clés NSMainNibFile~ipad et NSMainNibFile~iphone à votre Info.plist. Vous pouvez également ajouter un NIB MainWindow~ipad.xib à votre cible. Il sera chargé sur l'iPad au lieu de MainWindow.xib, en fonction de la clé NSMainNibFile dans Info.plist.

Si vous avez besoin de plus de contrôle et de personnalisation pour une application universelle, vous pouvez charger la NIB de démarrage manuellement. Le modèle de projet "Universal" a le standard utilisé pour cette méthode. Le moyen le plus rapide de commencer à utiliser cette technique consiste donc simplement à créer un nouveau projet iOS avec le profil Universal.

Dans les exemples ci-dessus, le Main NIB File est défini dans Info.plist (paramètres de cible), de sorte qu'un NIB sera déjà chargé lorsque votre délégué à l'application est appelé. Généralement, dans cette configuration, un objet MyAppDelegate sera également archivé dans la NIB (avec une certaine IBOutlets) et le File's Owner de la NIB sera défini sur UIApplication.

Pour qu'un projet universel puisse prendre en charge deux dispositions différentes, la clé Main NIB File (Fichier principal de la carte d'interface réseau) est laissée sur Info.plist. Ensuite, il instancie par programme l’objet délégué d’application dans UIApplicationMain:

#import "MYAppDelegate.h"

int main(int argc, char *argv[])
{
  @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([MYAppDelegate class]));
  }
}

Ensuite, vérifiez votre environnement et vos paramètres, puis chargez le fichier NIB approprié dans application:DidFinishLaunchingWithOptions:.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  _window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
  // Override point for customization after application launch.
  if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
    _viewController = [[[MYViewController alloc] initWithNibName:@"MYViewController_iPhone" bundle:nil] autorelease];
  } else {
    _viewController = [[[MYViewController alloc] initWithNibName:@"MYViewController_iPad" bundle:nil] autorelease];
  }
  _window.rootViewController = _viewController;
  [_window makeKeyAndVisible];
  return YES;
}

- (void)dealloc {
  [_window release];
  [_viewController release];
  [super dealloc];
}

La nouvelle étape consiste à créer une racine MYViewController manuellement, en chargeant le fichier NIB approprié. Dans cette configuration, le File's Owner est votre tout nouveau MYViewController plutôt que UIApplication. Si vous le souhaitez, MYViewController peut adopter une grande partie de ce que vous avez peut-être utilisé pour déléguer votre application, à savoir encapsuler la classe de modèle principale de l'application, agir en tant que source de données et déléguer pour les vues et autres éléments de la NIB.

On s'attend donc à ce que la NIB contienne une racine UIView et elle devrait être connectée à la sortie view du File's Owner (MYViewController).

Notez que la NIB de MYViewController n'est chargée en réalité que lors du premier accès à la propriété MYViewController.view. Alors seulement, [MyViewController viewDidLoad] sera appelé! Le moment le plus probable pour que cela se produise est lorsque vous l'ajoutez à la fenêtre racine.

Dans le code de modèle présenté ci-dessus, la racine UIWindow est instanciée par le délégué de l'application, mais rien n'empêche de l'inclure à la place dans votre NIB. Si vous choisissez de le faire, soyez prudent. Si vous définissez la variable rootViewController de la fenêtre de la NIB sur le propriétaire du fichier, dans ce cas, la vue du contrôleur sera ajoutée à la fenêtre lorsque la fenêtre est activée. Soyez prudent en construisant ce premier NIB dans tous les cas.

Si vous souhaitez que MYViewController le gère, le délégué de l'application n'a pas nécessairement besoin d'une référence à votre racine UIWindow, mais il peut être plus propre de garder la fenêtre racine hors de vos NIB et de la gérer dans le délégué de l'application.

En dehors de cela (!), L’approche de la plate-forme unique n’est pas très différente.

1
Scott Lahteine