Какая связь между AppDelegate, RootViewController и UIApplication?
Я пытаюсь выяснить взаимосвязь между appdelegate, RootViewControoler и UIApplication. Вот что я догадался до сих пор:
При запуске приложения main.m загружается.
Здесь загружается ваш MainWindow.xib.
В вашем MainWindow.xib владелец файла имеет тип UIApplication.
Вы назначили делегата UIApplication в AppDelegate.
В исходном коде AppDelegate вы можете установить, что ваш RootViewController является первым показанным видом.
Правильно ли это? Что подсказывает AppDelegate для его первоначального запуска
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { }
метод?
Ответы
Ответ 1
При запуске приложения Objective-C он запускается с помощью функции main(). Это не должно быть в файле "main.m", но это то, как мастер Xcode устанавливает вещи.
Внутри функции main(), созданной мастером, есть следующая строка:
int retVal = UIApplicationMain(argc, argv, nil, nil);
Это то, что запускает структуру "UIKit", которая составляет все приложение. Внутри UIApplicationMain создается объект типа UIApplication. И часть того, что UIApplication делает при запуске приложения, вызывает метод applicationDidFinishLaunchingWithOptions для члена делегата класса UIApplication. Этот делегат настроен в файле MainWindow.xib как экземпляр вашего класса ProjectAppDelegate, подкласса NSObject, который соответствует протоколу UIApplicationDelegate.
Что подсказывает AppDelegate изначально запустите его...
Так как в вашем файле MainWindow.xib вы подключились (ну, собственно, мастер проекта действительно подключил соединение) File Owner (который является объектом UIApplication), "делегировать" выход объекту UIApplicationDelegate в файле .xib, а класс UIApplicationDelegate настроен для вашего подкласса UIApplicationDelegate.
И нет ничего волшебного в отношении "MainWindow.xib", его можно было бы назвать "Foo.xib", что важно, что свойство в вашем файле Info.plist под названием "Main nib file base name" является "MainWindow". Попробуйте переименовать MainWindow.xib в Foo.xib и измените "Основное имя файла основного файла" в вашем Info.plist на "Foo", и вы увидите, что он все еще работает.
EDIT: больше о RootController
Опять же, нет ничего волшебного в так называемом "RootController". Это просто имя подкласса UIViewController, созданного для вас мастером нового проекта Xcode.
Мастер помещает код в проект для двух классов: ProjectAppDelegate и ProjectViewController. Класс ProjectAppDelegate содержит два члена:
IBOutlet UIWindow *window;
IBOutlet ProjectViewController *viewController;
в файле MainWindow.xib, размещаются экземпляры как UIWindow, так и ProjectViewController и подключены к вышеуказанным выходам в ProjectAppDelegate.
Что вы получаете на экране, это этот код в вашем классе ProjectAppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the view controller view to the window and display.
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
return YES;
}
Опять же, в этом нет ничего удивительного: мастер проекта создал код, который добавляет ваш "вид" ViewController к представлению окна и делает окно видимым. Ваш "корневой" контроллер представлений был создан в файле .xib и подключен к выходу ProjectAppDelegate.
Очень поучительно пытаться самостоятельно создать приложение самостоятельно, не используя ни одного из файлов мастера. Вы узнаете много о том, как работают файлы .xib и как они относятся к объектам кода.
Ответ 2
Исходной точкой приложений iOS всегда является функция main()
(спасибо @bogatyr), которая обычно содержит код, похожий на
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
Последние два параметра UIApplicationMain
важны и указывают имя основного класса и делегат приложения. Если они nil
, тогда Info.plist будет искать главное окно xib (обычно 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);
Нет необходимости устанавливать File Owner через xib, и они могут быть указаны непосредственно в этой функции UIApplicationMain
.
principalClassName
может быть строкой UIApplication
или подклассом UIApplication
. Аналогично delegateClassName
может быть непосредственно задан в этом методе. Класс делегата создается с использованием init
, как говорят документы. Предположим, что мы укажем наш класс делегата - MyAppDelegate
как строку,
UIApplicationMain(int argc, char *argv[], nil, @"MyAppDelegate");
Сначала создается экземпляр экземпляра UIApplication, который затем создает класс делегата из этой строки, используя NSClassFromString
Я полагаю.
После того как экземпляр делегированного объекта был создан, и приложение готово, этот делегат-объект будет проинформирован с использованием метода делегирования 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:...];
Вот как UIApplication будет обрабатывать его программно, если не использовать ниб. Использование ножа посередине не сильно отличается.
Ответ 3
MainWindow.xib определен в вашем info.plist как Main nib file base name
. В вашем MainWindow.xib вы определяете первый контроллер, который вы хотите загрузить, в вашем случае RootViewController
.
didFinishLaunchingWithOptions:
является частью протокола UIApplicationDelegate
. Этот метод (в iOS4.0 +) всегда известен как первый, который будет вызываться при запуске приложения.
Ответ 4
Так как ваш AppDelegate
является делегатом UIApplication - он прослушивает все уведомления, которые UIApplication
публикует сообщения класса во время жизненного цикла. didFinishLaunching
уведомление является одним из них, и это приводит к тому, что ваш AppDelegate
вызывает вышеупомянутый метод.
Ответ 5
Для Universal - iPhone + iPad - приложения вы можете указать, что разные загрузки NIB на каждой платформе либо на целевой информационной панели, либо путем добавления ключей NSMainNibFile~ipad
и NSMainNibFile~iphone
к вашему Info.plist
. Кроме того, вы можете добавить NIB MainWindow~ipad.xib
к своей цели, он будет загружен на iPad вместо MainWindow.xib
на основе ключа NSMainNibFile
в Info.plist.
Если вам нужно больше контроля и настройки для универсального приложения, вы можете загрузить стартовый NIB вручную. Шаблон проекта "Универсальный" имеет шаблон для этого метода, поэтому самый быстрый способ начать использовать эту технику - это просто создать новый проект iOS с универсальным профилем.
В приведенных выше примерах Main NIB File
устанавливается в Info.plist
(целевые настройки), так что вы уже будете загружать NIB при вызове делегата приложения. Обычно в этой установке объект MyAppDelegate
также будет архивироваться в NIB (с некоторым IBOutlets
), а NIB File Owner
будет установлен в UIApplication
.
Для универсального проекта, позволяющего разместить два альтернативных макета, ключ основного файла NIB остается вне Info.plist
. Затем он программным образом создает объект делегирования приложения в UIApplicationMain
:
#import "MYAppDelegate.h"
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MYAppDelegate class]));
}
}
Затем проверьте свою среду и настройки и загрузите соответствующий NIB в 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];
}
Новый шаг - создать root MYViewController
вручную, загрузив соответствующий NIB. В этой настройке File Owner
- ваш новый новый MYViewController
, а не UIApplication
. Если вы хотите, MYViewController
может принять большую часть того, что вы, возможно, использовали для делегирования вашего приложения, что часто заключается в инкапсуляции класса базовой модели приложения, выступать в качестве источника данных и делегировать для представлений и других вещей в СИБ.
Таким образом, вы ожидаете, что в NIB есть корень UIView
, и он должен быть подключен к выходу view
File Owner
(MYViewController
).
Обратите внимание, что NIB MYViewController фактически не загружается до тех пор, пока не будет доступ к свойству MYViewController.view
. Только тогда будет называться [MyViewController viewDidLoad]
! Вероятнее всего, это произойдет, когда вы добавите его в корневое окно.
В приведенном выше шаблоне код root UIWindow
создается делегатом приложения, но нет причин, по которым вы не могли бы включить его в свой NIB. Если вы решите сделать это, будьте осторожны. Если вы установите rootViewController
окна в NIB владельцу файла в этом случае, это приведет к тому, что представление контроллера будет добавлено в окно при активации окна. Будьте осторожны, построив первый NIB в любом случае.
Делегат приложения необязательно должен иметь ссылку на ваш root UIWindow
, если вы хотите, чтобы MYViewController управлял им, но может быть более чистым в целом, чтобы оставить корневое окно из ваших NIB и управлять им в приложении делегировать.
Вне этого (!) не сильно отличается от одноплатформенного подхода.