2011-02-10 16 views
25

Sto cercando di capire la relazione tra appdelegate, RootViewControoler e UIApplication. Ecco cosa ho scoperto finora:Qual è la relazione tra AppDelegate, RootViewController e UIApplication?

All'avvio dell'applicazione, main.m viene caricato.

Da qui, il MainWindow.xib viene caricato.

In MainWindow.xib, il proprietario del file è di tipo UIApplicazione.

Imposta il delegato della tua UIApplication sul tuo AppDelegate.

Nel codice sorgente di AppDelegate, è possibile impostare RootViewController come prima visualizzazione.

È giusto? Ciò che richiede AppDelegate inizialmente è

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

metodo?

+1

che è giusto !, "didFinishLaunchingWithOptions" è il primo metodo da eseguire! –

risposta

44

All'avvio di un'applicazione Objective-C, viene avviata eseguendo la funzione denominata main(). Non deve essere nel file "main.m" ma è così che la procedura guidata Xcode imposta le cose.

All'interno della funzione wizard-prodotto principale(), c'è questa riga:

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

Questo è ciò che inizia il quadro "UIKit" che rende l'intera applicazione. All'interno di UIApplicationMain, viene creato un oggetto di tipo UIApplication. E parte di ciò che fa UIApplication all'avvio dell'applicazione è chiamare il metodo applicationDidFinishLaunchingWithOptions sul membro delegato della classe UIApplication. Questo delegato viene impostato nel file MainWindow.xib come un'istanza della classe ProjectAppDelegate, una sottoclasse di NSObject conforme al protocollo UIApplicationDelegate.

Che cosa spinge a AppDelegate inizialmente run è ...

Perché nel file MainWindow.xib è stato collegato (bene il wizard di progetto ha fatto la connessione in realtà) il proprietario del file (che è il L'uscita "delegato" di UIApplication object) all'oggetto UIApplicationDelegate nel file .xib e la classe di UIApplicationDelegate è impostata sulla sottoclasse UIApplicationDelegate dell'app.

E non c'è nulla di magico in "MainWindow.xib", potrebbe essere chiamato "Foo.xib", l'importante è che la proprietà nel file Info.plist denominata "Nome file base del pennino principale" sia "MainWindow". Provando a rinominare MainWindow.xib su Foo.xib e cambiando il "Nome base del file di pennini principale" nel tuo Info.plist a "Foo", vedrai che funziona ancora.

EDIT: più su RootController

Anche in questo caso, non c'è niente di magico il cosiddetto "RootController". Questo è solo il nome della sottoclasse UIViewController creata per te dal wizard del nuovo progetto Xcode.

La procedura guidata inserisce il codice nel progetto per due classi: ProjectAppDelegate e ProjectViewController. La classe ProjectAppDelegate contiene due membri di uscita:

IBOutlet UIWindow *window; 
IBOutlet ProjectViewController *viewController; 

nel file MainWindow.xib, istanze sia UIWindow e ProjectViewController sono posti, e collegato alle prese sopra in ProjectAppDelegate.

Cosa ottiene il vostro roba sullo schermo è il codice nella 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; 
} 

Anche in questo caso, nulla di veramente magico in questo: la procedura guidata di progetto ha creato il codice che aggiunge il vostro "root" punto di vista di ViewController al vista della finestra e rende visibile la finestra. Il controller di visualizzazione "root" è stato creato nel file .xib e collegato all'outlet ProjectAppDelegate.

È molto istruttivo provare a creare un'applicazione completamente da solo senza utilizzare alcun file dalla procedura guidata. Imparerai molto su come funzionano i file .xib e su come si relazionano agli oggetti codice.

+0

Ho appena svitato questo modo per vederti passare da 9997 a 10k: p. Non male come risposta. –

+0

w00t :) Fantastico, grazie – Bogatyr

1

MainWindow.xib è definito nel tuo info.plist come Main nib file base name. In MainWindow.xib, si definisce il primo controller che si desidera caricare, nel proprio caso, RootViewController.

didFinishLaunchingWithOptions: è parte del protocollo UIApplicationDelegate. Questo metodo (in iOS4.0 +) è sempre noto per essere il primo a essere chiamato quando si avvia un'applicazione.

1

Poiché il tuo AppDelegate è un delegato di UIApplication - ascolta tutte le notifiche che i messaggi di classe UIApplication durante il suo ciclo di vita. La notifica è una di queste e causa il tuo AppDelegate per chiamare il metodo sopra menzionato.

+5

Bene, il delegato in realtà non "ascolta" la notifica (come con la registrazione come osservatore a NSNotificationCenter). Invece, UIApplication controlla se il delegato implementa vari metodi usando 'respondsToSelector:' e li chiama se esistono (che devono avere esattamente il nome predefinito, al contrario di ascoltare una notifica in cui è possibile nominare il selettore di destinazione da soli). – DarkDust

15

Il punto di partenza di applicazioni IOS è sempre la funzione main() (grazie @bogatyr) che solitamente contiene il codice simile,

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

Gli ultimi due parametri di UIApplicationMain sono importanti e specificare il nome della classe principale, e il delegato dell'applicazione. Se sono nil, quindi Info.plist sarà cercato per la finestra principale xib (di solito 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); 

Non è necessario impostare il file proprietario attraverso xib, e possono essere specificati direttamente in questa funzione UIApplicationMain.

principalClassName può essere la stringa UIApplication o una sottoclasse di UIApplication. Allo stesso modo delegateClassName può essere specificato direttamente in questo metodo. La classe del delegato viene istanziata utilizzando init come dicono i documenti. Supponiamo di specificare la nostra classe delegato - MyAppDelegate come una stringa,

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

Prima un'istanza di UIApplication viene istanziato, che poi creare la classe delegato di questa stringa utilizzando NSClassFromString suppongo.

Dopo l'istanza di delegateObject e l'applicazione è pronta, questo oggetto delegato verrà informato utilizzando il metodo 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:...]; 

Questo è il modo in UIApplication avrebbe gestito a livello di codice, se non viene utilizzato alcun pennino. L'uso di un pennino nel mezzo non è molto diverso.

+0

Non c'è nulla di magico su main.m, potrebbe essere chiamato foo.m, il punto di partenza è la funzione int main (int argc, char * argv []). – Bogatyr

+0

@Bogatyr - sì è la funzione 'main()' che è il punto di partenza. N Non c'è niente di speciale in 'main.m' come hai detto tu. – Anurag

1

Per universale - iPhone + iPad - le applicazioni è possibile specificare che diversi ONA carico su ogni piattaforma, sia nel target pannello Info o con l'aggiunta di NSMainNibFile~ipad e NSMainNibFile~iphone chiavi della vostra Info.plist. In alternativa, è possibile aggiungere un NIB MainWindow~ipad.xib alla destinazione, verrà caricato su iPad anziché su MainWindow.xib, in base alla chiave NSMainNibFile in Info.plist.

Se è necessario più controllo e personalizzazione per un'app universale, è possibile caricare manualmente la NIB iniziale. Il modello di progetto "Universale" ha il boilerplate per questo metodo, quindi il modo più rapido per iniziare a utilizzare questa tecnica è creare semplicemente un nuovo progetto iOS con il profilo Universal.

Negli esempi sopra il Main NIB File si trova in Info.plist (impostazioni di destinazione) in modo che si avrà già un pennino caricati quando il delegato applicazione viene chiamata. Di solito in questa configurazione viene archiviato anche un oggetto MyAppDelegate nella NIB (con qualche IBOutlets) e il NIB File's Owner verrà impostato su UIApplication.

Per un progetto universale in grado di supportare due layout alternativi, il tasto Main NIB File viene lasciato fuori da Info.plist. Poi istanzia l'oggetto applicativo delegato programmazione in UIApplicationMain:

#import "MYAppDelegate.h" 

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

quindi controllare l'ambiente e le impostazioni e caricare la NIB appropriato 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]; 
} 

Il nuovo passo è quello di creare una radice MYViewController manualmente, loading la NIB appropriata. In questa configurazione lo File's Owner è il tuo nuovo splendente MYViewController anziché UIApplication. Se lo desideri, lo standard MYViewController può adottare gran parte di ciò che potresti aver utilizzato per il delegato dell'applicazione, che spesso incapsula la classe del modello principale dell'app, funge da origine dati e delegato per le visualizzazioni e altre cose nella NIB.

Quindi ci si aspetta di avere qualche radice UIView nel pennino, e dovrebbe essere collegato alla view uscita del File's Owner (MYViewController).

Si noti che il NIB di MYViewController non viene effettivamente caricato fino al primo accesso alla proprietà MYViewController.view. Solo allora sarà chiamato [MyViewController viewDidLoad]! Il momento più probabile in cui ciò si verifichi è quando lo aggiungi alla finestra principale.

Nel codice modello mostrato sopra la radice UIWindow viene creata un'istanza dal delegato dell'app, ma non c'è motivo per cui non sia possibile includerla nella NIB. Se scegli di farlo, fai attenzione. Se si imposta rootViewController della finestra nella NIB sul proprietario del file in quel caso, la vista del controller verrà aggiunta alla finestra quando viene attivata la finestra. Fai attenzione a costruire quel primo NIB in ogni caso.

Il delegato dell'app non deve necessariamente avere un riferimento alla root UIWindow se si desidera che MYViewController lo gestisca, ma potrebbe essere più pulito in generale per mantenere la root window fuori dai propri NIB e gestirla nell'app delegato .

Al di fuori di questo (!) Non c'è molto diverso dall'approccio a piattaforma singola.

Problemi correlati