Programmieren mit Swift - Für macOS und iOS
Programmieren mit Swift - Für macOS und iOS
NSApplication und Delegate

Wenn Sie schon Erfahrungen mit den Programmiersprachen C oder C++ haben, wissen Sie auch, dass jedes Programm mit der main-Methode beginnt. Das ist auch bei einem Objective-C Programm nicht anders, obwohl Sie bei Ihrer ersten Cocoa Anwendung von der main-Methode noch nicht viel gesehen haben. Trotzdem gib es auch hier eine solche Methode. Sie wurde automatisch von Xcode angelegt.
 
Die main-Methode findet sich in der main.m Datei in der Gruppe Other Sources.
int main(int argc, char *argv[])
{
    return NSApplicationMain(argc, (const char **) argv);
}
Hier gibt es nur eine Anweisung, nämlich NSApplicationMain. Dieser Aufruf erzeugt eine Instanz von NSApplication, von der jede Cocoa Anwendung auch nur genau eine Instanz hat.

NSApplication hat unter anderem die Aufgabe, die so genannte event Loop zu verwalten. Darunter versteht man eine Art Endlosschleife, mit der die Anwendung permanent Benutzereingaben über Maus und Tastatur und anderen Eingabemedien abfragt.
Außerdem wird durch NSApplicationMain, die erste NIB-Datei der grafischen Oberfläche geladen und angezeigt.
Den Aufruf von NSApplicationMain kann man sich sinngemäß etwa wie folgt vorstellen. 
void NSApplicationMain(int argc, char *argv[]) {

    [NSApplication sharedApplication];

    [NSBundle loadNibNamed:
@"myMain" owner:NSApp];

    [NSApp run];

}
Außerdem wird durch diesen Aufruf eine globale Variable NSApp erzeugt, mit der man im Programm Zugriff auf die von NSApplication erzeugte Instanz erhält, falls das nötig sein sollte.
 
Veränderungen muss man an der main-Methode einer Cocoa Anwendung nur selten vornehmen, weniger ungewöhnlich ist es, der NSApplication ein Delegate-Objekt zu zuweisen.
 
Um zu verstehen, was ein Delegate-Objekt ist, muss man sich kurz ins Gedächtnis rufen, wie Objective-C funktioniert. Objekte kommunizieren miteinander, indem sie sich gegenseitig Nachrichten senden. Möchte man eine bestimmte Methode in einem bestimmten Objekt aufrufen, erhält das Objekt eine Nachricht mit dem Auftrag, das zu tun. Eventuell nötige Parameter können ebenfalls mit übertragen werden.
 
Objective-C erlaubt es, jedem beliebigen Objekt jede beliebige Nachricht zu senden. Zwar wird der Compiler in den meisten Fällen warnen, wenn eine Methode aufgerufen werden soll, die nicht implementiert ist, aber prinzipiell kann man alle Nachrichten senden. Wenn nötig, kann man sogar zur Laufzeit ermitteln, ob ein Objekt auf eine Nachricht reagieren wird. Der Aufruf respondsToSelectorkann kann dafür verwendet werden.
 
Durch ein Delegate kann nun ein Objekt (Objekt-A) eine anderes Objekt (Objekt-D) beauftragen, bestimmte Aufgaben zu erledigten, indem Objekt-A Nachrichten an Objekt-D sendet. Wenn Objekt-D auf diese Nachrichten reagiert, also wenn passende Methoden implementiert sind, werden diese ausgeführt. Gibt es keine passende Methode oder gibt es auch kein Delegate-Objekt, passiert nichts. Der Entwickler ist nicht verpflichtet, alle von Objekt-A gesendeten Nachrichten in Objekt-D auch auszuwerten, nur weil es als Delegate-Objekt festgelegt wurde.
 
Delegation trifft man sehr häufig in Objective-C. Statt von einer Klasse abzuleiten, genügt es oft, ein bestehendes Objekt um ein Delegate-Objekt zu erweitern. Das setzt natürlich voraus, dass ein Objekt auch immer Nachrichten sendet, wenn bestimmte Ereignisse auftreten. Aber sehr viele Cocoa Klassen sind so konzipiert, um mit Delegation zu arbeiten. In späteren Lektionen werden Sie einige nützliche Funktionen kennenlernen, die ohne Delegation nur schwer zu realisieren wären. Auch die Instanz von NSApplication kann um eine Delegate-Objekt erweitert werden, denn auch NSApplication sendet verschiedene Nachrichten.
 
Haben Sie ihr Projekt mit Xcode für Snow Leopard erzeugt, wird die automatisch erstellte Controller-Klasse auch automatisch der Delegate von NSApplication. Daher kommt auch der Name HalloWeltAppDelegate für die erstellten Dateien. Da Sie aber mit MyController in diesem Beispiel den Controller selbst angelegt haben, müssen Sie ihn auch selbst zum Delegate von NSApplication machen. Das geht mit weniger Handgriffen im Interface Builder.

Markieren Sie zunächst im Hauptfenster des Interface Builders das Symbol Files‘s Owner. Damit ist bei diesem Projekttype die Instanz von NSApplication gemeint.
stacks_image_DF1D77E8-7034-4A83-A8C7-76071608B07B
Aktivieren Sie anschliessend im Inspector die Gruppe Connections. Wie Sie sehen, ist der Delegate momentan noch HalloWeltAppDelegate und vielleicht haben Sie auch bemerkt, dass es auch von dieser Klasse eine Instanz im Hauptfenster gibt. Wie Anfangs erwähnt, wurden all diese Dinge von Xcode automatisch erzeugt, Sie können Sie aber weiterhin ignorieren und den Delegate selbst festlegen.
stacks_image_B436FE91-2CC3-43A9-9F1C-9477701A821D
Klicken Sie zunächst im Inspector auf das X zwischen delegate und HalloWeltAppDelegate, um die Verbindung zu trennen. Die Verbindung zwischen Delegate-Objekten werden genau so gezeichnet wie zuvor mit den Action und Outlet. Ziehen Sie deshalb eine Verbindung vom Inspector zu der Instanz von MyController.
stacks_image_416F5FD3-A20A-4E45-B3DD-5EC4D3D58D06
Im Inspector sollte jetzt auch MyController als Delegate für den File‘s Owner angezeigt werden. Ab jetzt sendet das Application-Objekt seine Nachrichten an die Instanz von MyController, falls Sie nicht vergessen haben, die Änderungen zu speichern.

Welche Nachrichten das im Detail sind, entnehmen Sie der Dokumentation von Apple. An dieser Stelle wollen wir uns nur mit einer Nachricht beschäftigen, nämlich applicationShouldTerminateAfterLastWindowClosed.

Im Unterschied zu Programmen auf Windows-Systemen läuft eine Cocoa Anwendung auch dann oft noch weiter, wenn alle Programmfenster geschlossen wurden. Versuchen Sie es mit iCal oder dem Adressbuch. Die Menüleiste am Fensterrand bleibt bestehen, selbst wenn Sie das Fenster des Programms schliessen.
Aber nicht alle Programme reagieren so. Der Rechner zum Beispiel wird beendet, wenn Sie sein Fenster schliessen. Und das ist auch sinnvoll, denn im Unterschied zum Adressbuch können Sie das Fenster des Rechners nicht wieder öffnen, nachdem es geschlossen wurde. Das Gleiche gilt für Ihre Hallo Welt Anwendung. Es wäre also nur konsequent auch Ihr Programm zu beenden, wenn das Fenster geschlossen wurde.

Um das zu erreichen, kann man sich einer Nachricht bedienen, die NSApplication an sein Delegate-Objekt sendet, und mit der nachgefragt wird, was zu tun ist. Implementieren Sie folgende Methode in der MyController.m Datei. Eine Deklaration in der Headerdatei ist nicht notwendig.
-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *) theApp
{
    return YES;
}
Beim Schreiben muss man hier besonders aufmerksam sein, damit sich keine Fehler in die Methodensignatur einschleichen. Ein falsches Zeichen würde genügen und die Methode würde nicht mehr aufgerufen, da sie nicht mehr der Nachricht entspricht. Solche Fehler kann man leider erst zu Laufzeit entdecken.

Wird jetzt das letzten Fenster der Anwendung geschlossen- und es gibt ja nur ein Fenster- sendet NSApplication eine Nachricht an sein Delegate-Objekt mit der Frage, was zu tun ist. Gibt die Methode YES zurück, wird die Anwendung geschlossen. Bei NO als Rückgabewert läuft die Anwendung weiter.