プログラムのエントリーポイントと初期化シーケンス
ここでは、ウィンドウを表示するだけの単純なプログラムを作り、プログラムの初期化がどのようにどう行われるのかみてみましょう。
テーマがテーマだけに、詳しく掘り下げようと思うとキリがありませんので、大まかな流れを理解するものとして考えてください。 より正確には Apple の資料を読んでください。
Xcode で Single View Application テンプレートでテスト用のプログラムを作成します。
作成されたファイルのなかで、特に次の三つのファイルの注目しましょう。
AppDelegate.swift では AppDelegate というクラスが定義されています。
AppDelegate は UIApplicationDelegate プロトコルをコンフォームしているクラス で、これにより システムとアプリケーション間で起きる、様々なイベントを捉えることができます。
デリゲート(delegate)というのは、オブジェクト指向プログラミングのデザインパターンのひとつ (delegate pattern) の言葉ですが、 要は何らかのコールバックを受け取るオブジェクトのことです。AppDelegate はシステムから、 アプリケーションが起動するとか、終了するといったイベントに関わるコールバックを受け取るオブジェクトということになります。
ViewController.swift ではユーザーとアプリケーション間のやり取りの管理を行います。 Main.storyboard では画面遷移とビューコントローラとの関連付けを行なっています。
プログラムのエントリーポイント〜 @UIApplicationMain
さて、Cocoa Touch フレームワークではプログラムのエントリーポイントは UIApplicationMain 関数ということになってます。 細かく言えばその前に main 関数があって、main 関数から UIApplicationMain 関数が呼び出されます。
エントリーポイントとは?
例えば C 言語の場合には、プログラムのエントリーポイントとなるのは main 関数ということになっています。 Windows アプリケーションであればエントリポイントは基本的に WinMain という名前の関数だったりします。
「プログラムが起動される」というのは、起動環境の準備などを除いてざっくりいえば 「システムによってエントリーポイントとなる関数が呼び出されること」といえます。そして、エントリーポイント関数が返る(終わる)ということが、 アプリケーションが終了するということです。
ところが、生成されたコードを見てもどこにも UIApplicationMain 関数はありません。これはなぜでしょうか? プログラムを開始するポイントであるはずの関数がないのに、いったいどうやってプログラムが起動しているのでしょうか?
実はこの謎を解く鍵は AppDelegate クラスに設定されている @UIApplicationMain 属性にあります。
よく見ると、確かについてますね。
そもそも UIApplicationMain(_:_:_:_:) 関数を呼び出すための引数のうち、ユーザーが明示的に指定しないといけないのは通常、 アプリケーションデリゲートのクラス名だけです。つまり、アプリケーションデリゲートがどのクラスであるか指定できれば、プログラムを起動するための情報として十分なのです。
AppDelegate.swift で AppDelegate クラスに @UIApplicationMain 属性をつけることにより、コンパイラに対し、 これが UIApplicationMain 関数で使うアプリケーションデリゲートであることを教えています。これによって、 暗黙的に UIApplicationMain(_:_:_:_:) 関数を呼び出すことが可能になります。
このため、UIApplicationMain(_:_:_:_:) 関数は明示的に存在していないのです。
ちなみに、main.swift という Swift ファイルを作り、そこに UIApplicationMain(_:_:_:_:) 関数を明示的に記述することも可能です。 このときには AppDelegate クラスに @UIApplicationMain 属性がついているとコンパイル時にエラーになります。
プログラムのスタートアップシーケンス
UIApplicationMain 関数が呼ばれてプログラムが開始したら、まずアプリケーションを表す UIApplication クラスのインスタンスを作ります。 このインスタンスは UIApplication.shared にセットされます。
そして、アプリケーションデリゲート・オブジェクトを、その UIApplication オブジェクトのデリゲートにセットします。 (したがって、UIApplication.shared.delegate からデリゲートオブジェクトにもアクセス可能)
次にルート・ビューコントローラを作成するために、 plist.Info ファイルから、メインのストーリーボードを探します。 メインストーリーボードのイニシャルビューコントローラがルートビューコントローラになります。
通常は Main.storyboard がその名前の通り、メインのストーリーボードに指定されています。
メイン・ストーリーボードにて「イニシャル・ビューコントローラ」(Initial View Controller) に指定されているビューコントローラのインスタンスを作成します。
ストーリーボードの画面で言えば、シーンの横に表示されている矢印があるものが「イニシャル・ビューコントローラ」です。
また、アトリビュートインスペクタでは Is Initial View Controller にチェックされているビューコントローラが「イニシャル・ビューコントローラ」です。
そこで UIWindow のインスタンスを作成し、そのインスタンスの rootViewController プロパティにイニシャルビューコントローラのインスタンスをセットします。
この UIWindow オブジェクトはアプリケーションデリゲートオブジェクトの、window プロパティにセットされます。
このため AppDelegate には、ソッと window プロパティが用意されていたのでした。
一見、誰も初期化する人がいないようにみえるのに、ちゃんと window プロパティに値がセットされているのはこのためです。
ここまで、スタートアップシーケンスで下準備をしてから、いよいよ、アプリケーションデリゲートの application(_:, didFinishLaunchingWithOptions:) が呼ばれることになります。
このため、application(_:, didFinishLaunchingWithOptions:) では既にルートビューコントローラなどが初期化されていることを前提として良いことになります。
以上、ここではプログラムのエントリーポイントから、アプリケーションデリゲートの application(_:, didFinishLaunchingWithOptions:) メソッドが呼ばれるところまでの初期化シーケンスの概要を説明しました。