В процессе сборки исходные файлы, подлежащие компиляции для создания бинарного приложения, известны, потому что они перечисляются в фазе сборки целевого приложения Compile Sources.

(В этом случае наш проект Empty Window содержит файлы ViewController .m, AppDelegate.m и main.m.) Кроме того, в ходе компиляции используется предварительно скомпилированный заголовок; фактически он обрабатывается до всех остальных исходных файлов. На фазе сборки Compile Sources этот файл не указывается; цель знает о нем, потому что он указан в настройках сборки Prefix Header. (В проекте Empty Window этот файл называется Empty Window-Pref ix .pch.)

Предварительно скомпилированный заголовок — это средство ускорения компиляции. Это заголовочный файл; он компилируется только один раз (или, по крайней мере, очень редко), а результаты компиляции кешируются (в папке DerivedData) и неявно импортируются во все исходные файлы. Таким образом, предварительно скомпилированные заголовки, которые никогда не изменяются (например, встроенные заголовки каркаса Cocoa), должны состоять в основном из директив #import; это удобное место для включения директивы tdefines, которая никогда не изменяется и должна использоваться всеми исходными файлами, как указано в главе 4. По умолчанию предварительно скомпилированный заголовочный файл импортирует файлы Foundation.h (заголовочный файл каркаса Core Foundation) и UIKit. h (заголовочный файл каркаса Cocod). Они будут рассмотрены в следующем разделе.

При запуске приложения система знает, где найти бинарный код в комплекте приложения, потому что файл Info.plist содержит ключ “Executable file” (CFBundleExecutable), значение которого сообщает системе имя бинарного кода; по умолчанию имя бинарного кода определяется значением переменной окружения EXECUTABLE_NAME (в данном случае “Empty Window”).

Приложение — это программа на языке Objective-C. В свою очередь, язык Objective-C является подмножеством языка С, поэтому точкой входа является функция main. Эта функция определена в файле проекта main .т. Приведем пример функции main:

int main(int argc, char *argv[])

{

@autoreleasepool ( return UIApplicationMain(argc, argv, nil,

NSStringFromClass([AppDelegate class]));

)

}

Функция main делает две вещи.

  • Настраивает окружение системы управления памятью с помощью директивы @autoreleasepool и фигурных скобок, которые ее сопровождают. Их смысл разъясняется в главе 12.
  • Вызывает функцию UIApplicationMain, которая разворачивает ваше приложение и запускает его.

Функция UIApplicationMain решает несколько сложных проблем. Где взять первичные экземпляры для вашего приложения? Какие методы экземпляра следует вызвать из этих экземпляров? Где ваше приложение должно искать начальный интерфейс? На эти вопросы отвечает функция UIApplicationMain.

1.            Функция UIApplicationMain создает первый экземпляр — общий экземпляр приложения (shared application instance), к которому можно обращаться из кода с помощью вызова [UIApplication sharedApplication]. Третий аргумент функции

UIApplicationMain, представляющий собой строку, задает имя класса, к которому должен относиться общий экземпляр вашего приложения. Если этот параметр равен nil, что является довольно распространенным случаем, по умолчанию используется класс UIApplication. Если по каким-то причинам вам нужен подкласс класса UIApplication, вы должны указать этот подкласс здесь, выполнив замену (она зависит от того, как называется подкласс) третьего аргумента в вызове функции UIApplicationMain.

NSStringFromClass([MyUIApplicationSubclass class])

2. Функция UIApplicationMain создает второй экземпляр — делегата экземпляра приложения. Делегирование — важный шаблон каркаса Cocoa, подробно описанный в главе 11. Очень важно, чтобы каждое приложение, созданное вами, имело экземпляр делегата приложения. Четвертый аргумент в вызове функции UIApplicationMain задает в виде строки имя класса, к которому должен относиться экземпляр делегата приложения. В нашем файле эта спецификация выглядит следующим образом:

NSStringFromClass([AppDelegate class])

Она приказывает функции UIApplicationMain создать экземпляр класса AppDelegate и связать его с общим экземпляром приложения в качестве его делегата. Исходные файлы этого класса, AppDelegate. h и AppDelegate .m, создаются шаблоном; разумеется, вы можете (и, вероятно, будете) редактировать этот код.

3. Если в файле Info.plist указан главный файл раскадровки, функция UIAppli-cationMain загружает его и ищет в нем контроллер представления, назначенный контроллером начального представления данной раскадровки; она создает экземпляр этого контроллера представления, тем самым создавая третий экземпляр приложения. В случае проекта Empty Window, созданного по шаблону Single View Application, этим контроллером представления является экземпляр класса ViewController; исходные файлы, определяющие этот класс, ViewController. h и ViewController .m, также создаются шаблоном.

4. Если главный файл раскадровки существует, то функция UIApplicationMain создает окно приложения, создавая экземпляр класса UlWindow, — это уже четвертый экземпляр приложения. Функция назначает этот экземпляр окна свойству делегата window; она также назначает начальный экземпляр контроллера представления корневым контроллером представления этого экземпляра окна (свойство rootViewController).

(Я упрощаю. На самом деле функция UIApplicationMain сначала дает экземпляру делегата приложения возможность создать экземпляр окна, вызвав свой метод window. Именно пользовательский код создает экземпляр подкласса класса UlWindow.)

5. Затем функция UIApplicationMain переключается на экземпляр делегата приложения и начинает вызывать его методы — в частности, метод application: didFinis hLaunchingWithOptions :. Это дает возможность выполнить ваш код! Таким образом, метод application: didFinishLaunchingWithOptions: — хорошее место для размещения вашего кода, инициализирующего значения и выполняющего запуск, но при этом этот код должен выполняться быстро, потому что ваш интерфейс еще не появился на экране.

(Я снова упрощаю. Начиная с версии iOS 6, последовательность вызовов кода делегата приложения на само деле начинается с метода application: willFinishLaunchin gWithOptions:, если он существует.)

6. Если главная раскадровка существует, то функция UIApplicationMain выводит на экран интерфейс приложения. Для этого она вызывает метод экземпляра

makeKeyAndVisible класса UlWindow.

7. Теперь окно готово появиться на экране. Это, в свою очередь, заставляет окно обратиться к своему корневому контроллеру представления и запросить у него главное представление, которое будет занимать окно. Если контроллер представления получает свое главное представление из файлов . storyboard или .xib, то загружается nib-файл; его объекты создаются, инициализируются и становятся объектами начального интерфейса.

Теперь приложение запускается и становится видимым пользователю. Оно имеет несколько экземпляров — как минимум, общий экземпляр приложения, окно, контроллер начального представления и представление контроллера начального представления и все, что содержат объекты интерфейса. Часть вашего кода (метод делегата application: didFinishLaunchi ngWithOptions :) уже выполнена, а метод UIApplicationMain продолжает выполняться (метод UIApplicationMain никогда ничего не возвращает), отслеживая действия пользователя и поддерживая цикл событий, возникающих вследствие действий пользователя.

Описывая процесс запуска приложения, я несколько раз использовал выражение “если главная раскадровка существует.” В большинстве шаблонов Xcode 5, таких как Single View Application, который мы использовали для создания проекта Empty Window, главная раскадровка существует. Впрочем, шаблон может не содержать главную раскадровку. В этом случае такие действия, как создание экземпляра окна, назначение корневого контроллера представления, назначение свойства window делегата приложения и вызов метода makeKeyAndVisible из окна для демонстрации интерфейса, должен выполнять ваш код.

Для того чтобы понять, что я имею в виду, создайте новый проект для iPhone на основе шаблона Empty Application; назовем его Truly Empty. Он имеет класс Арр Delegate, но не имеет ни раскадровки, ни контроллера начального представления. Метод делегата приложения application:didFinishLaunchingWithOptions: в файле

AppDelegate.m содержит код для создания окна, присваивает это окно свойству делегата приложения window .

self.window = [[UlWindow alloc] initWithFrame:[[UlScreen mainScreen] bounds]];

// Точка замещения на настройки после запуска приложения,

self.window.backgroundColor = [UlColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

После сборки и запуска это приложение выводит пустое белое окно; но среда выполнения приложений выводит на консоль предупреждение “Application windows are expected to have a root view controller at the end of application launch” (“В конце процесса запуска окна приложения должны иметь корневой контроллер представления”). Это предупреждение можно предотвратить, создав контроллер представления и присвоив его свойству окна rootViewController.

self.window = [[UlWindow alloc] initWithFrame:[[UlScreen mainScreen] bounds]];

// Точка замещения на настройки после запуска приложения.

self.window.rootViewController = [UlViewController new]; // предотвращаем предупреждение

self.window.backgroundColor - [UlColor whiteColor];

[self.window makeKeyAndVisible] ;

return YES;

Все работает: при сборке и запуске приложения предупреждение больше не появляется. Однако у нас пока нет разумного способа настройки поведения приложения, так как нет подкласса класса UlViewController. Более того, у нас нет возможности для графического создания интерфейса; нет ни файла . storyboard, ни файла . xib. Обе эти проблемы можно решить, создав подкласс класса UlViewController вместе с файлом . xib.

1. Выберите команду FileONew^File. В диалоговом окне Choose a template в разделе iOS щелкните на пункте Cocoa Touch слева, выберите пункт Objective-С Class и щелкните на кнопке Next.

2. Присвойте классу имя ViewController и укажите, что он является подклассом класса UlViewController. Установите флаг With XIB for user interface. Щелкните на кнопке Next.

3. Откроется диалоговое окно Save. Убедитесь, что вы сохраняете проект в папке Truly Empty, что вплывающее меню Group также настроено на приложение Truly Empty и что выбрана цель Truly Empty, — мы хотим, чтобы эти файлы были частью целевого приложения. Щелкните на кнопке Create.

Среда Xcode создала для нас три файла; ViewController .h и ViewController .m, определив ViewController как подкласс класса UlViewController, а также файл ViewController. xib, из которого экземпляр класса ViewController автоматически получит свое представление по умолчанию.

4. Теперь вернемся к методу делегата приложения application :didFinishLaunchi ngWithOptions; в файле AppDelegate .m и изменим класс контроллера корневого представления ViewController; кроме того, в начало файла поместим директиву #import "ViewController.h".

self.window = [[UlWindow alloc] initWithFrame:[[UlScreen mainScreen] bounds]]; // Точка замещения на настройки после запуска приложения, self.window.rootViewController = [ViewController new]; // наш подкласс self.window.backgroundColor = [UlColor whiteColor];

[self.window makeKeyAndVisible]; return YES;

Таким образом, мы создали превосходный минимальный проект без раскадровки. Наш код выполняет работу, которую при наличии главной раскадровки класс UIApplicationMain выполняет автоматически: мы создаем экземпляр класса UlWindow, задаем экземпляр окна как свойство делегата window, создаем экземпляр контроллера начального представления, задаем экземпляр контроллера представления как свойство окна rootViewController и открываем окно на экране. Более того, появление окна автоматически заставляет экземпляр класса ViewController извлечь свое представление из nib-файла ViewController, который был скомпилирован из файла ViewController. xib; таким образом, мы можем использовать файл ViewController. xib для настройки начального интерфейса приложения.


 

 

 

Добавить комментарий