Какова связь между AppDelegate, RootViewController и UIApplication? - PullRequest
25 голосов
/ 10 февраля 2011

Я пытаюсь выяснить связь между appdelegate, RootViewControoler и UIApplication.Вот что я вроде как выяснил:

При запуске приложения загружается main.m.

Отсюда загружается ваш MainWindow.xib.

В вашем MainWindow.xib владелец вашего файла имеет тип UIApplication.

Вы устанавливаете делегата вашего UIApplication на свой AppDelegate.

В исходном коде вашего AppDelegate вы можете установить свой RootViewController в качестве первого показанного представления.

Это правильно?Что побуждает AppDelegate изначально запускать метод

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

?

Ответы [ 5 ]

44 голосов
/ 10 февраля 2011

Когда приложение 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 вы подключили (ну, на самом деле, мастер проекта установил соединение) выход «делегата» владельца файла (который является объектом UIApplication) к объекту UIApplicationDelegate вфайл .xib, а класс UIApplicationDelegate установлен в подкласс UIApplicationDelegate вашего приложения.

И в «MainWindow.xib» нет ничего волшебного, его можно назвать «Foo.xib», что важночто свойство в вашем файле Info.plist, называемое «Основное имя файла основного пера», является «MainWindow».Попытайтесь переименовать MainWindow.xib в Foo.xib и измените «Основное имя файла пера» в вашем Info.plist на «Foo», и вы увидите, что он все еще работает.

РЕДАКТИРОВАТЬ: больше о 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's view to the window and display.
    [self.window addSubview:viewController.view];
    [self.window makeKeyAndVisible];

    return YES;
}

Опять же, ничего особенного в этом нет: мастер проекта создал код, который добавляет ваш «корневой» вид ViewController к представлению окна,и делает окно видимым.Ваш «корневой» контроллер представления был создан в файле .xib и подключен к выходу ProjectAppDelegate.

Очень поучительно попытаться создать приложение полностью самостоятельно, без использования каких-либо файлов из мастера.Вы узнаете много нового о том, как работают файлы .xib и как они связаны с объектами кода.

15 голосов
/ 10 февраля 2011

Отправной точкой приложений для 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);

Нет необходимости устанавливать владельца файла через xib, и они могут быть указаны непосредственно в этой функции UIApplicationMain.

principalClassName может быть строкой UIApplicationили подкласс UIApplication.Точно так же delegateClassName может быть непосредственно указан в этом методе.Класс делегата создается с использованием init, как говорят документы.Предположим, мы указываем наш класс делегата - MyAppDelegate в виде строки,

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

Сначала создается экземпляр экземпляра UIApplication, который затем создает класс делегата из этой строки, используя NSClassFromString Полагаю.

Как только экземпляр DelegateObject был создан, и приложение готово, этот делегатObject будет проинформирован с использованием метода делегата, 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это программно, если ни одно перо не используется.Использование пера посередине не сильно отличается.

1 голос
/ 20 августа 2012

Для приложений Universal - iPhone + iPad - вы можете указать, что разные NIB загружаются на каждой платформе, либо на целевой информационной панели, либо добавив клавиши NSMainNibFile~ipad и NSMainNibFile~iphone к вашим Info.plist. Кроме того, вы можете добавить MainWindow~ipad.xib NIB к вашей цели, он будет загружен на iPad вместо MainWindow.xib, основываясь на ключе NSMainNibFile в Info.plist.

Если вам нужно больше контроля и настройки для универсального приложения, вы можете загрузить начальный NIB вручную. Шаблон проекта «Универсальный» содержит шаблон для этого метода, поэтому самый быстрый способ начать использовать эту технику - просто создать новый проект iOS с универсальным профилем.

В приведенных выше примерах Main NIB File установлен в Info.plist (целевые настройки), так что у вас уже будет загружен NIB при вызове делегата приложения. Обычно в этой настройке объект MyAppDelegate также будет заархивирован в NIB (с некоторыми значениями IBOutlets), а для NIB File's Owner будет установлено значение UIApplication.

Для универсального проекта, позволяющего разместить два альтернативных макета, ключ Main NIB File не указан в 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];
}

Новый шаг - создать корень MYViewController вручную, загрузив соответствующий NIB. В этой настройке File's Owner - это ваш новый MYViewController, а не UIApplication. Если вы хотите, MYViewController может принять большую часть того, для чего вы, возможно, использовали свой делегат приложения - который часто инкапсулирует базовый класс модели приложения, выступает в качестве источника данных и делегирует для представлений и других вещей в СИБ.

Таким образом, ожидается, что у вас есть корень UIView в NIB, и он должен быть подключен к view выходу File's Owner (MYViewController).

Обратите внимание, что NIB MYViewController фактически не загружается до тех пор, пока в первый раз не будет получен доступ к свойству MYViewController.view. Только тогда [MyViewController viewDidLoad] будет вызван! Наиболее вероятное время для этого - когда вы добавляете его в корневое окно.

В приведенном выше коде шаблона корень UIWindow создается делегатом приложения, но нет никаких причин, по которым вы не могли бы вместо этого включить его в NIB. Если вы решите сделать это, будьте осторожны. Если вы установите rootViewController окна в NIB для владельца файла в этом случае, это приведет к тому, что представление контроллера будет добавлено в окно, когда окно активировано. В любом случае будьте осторожны при создании первого NIB.

Делегат приложения не обязательно должен иметь ссылку на ваш корень UIWindow, если вы хотите, чтобы MYViewController управлял им, но в целом может быть чище, чтобы держать корневое окно вне ваших NIB и управлять им в приложении делегировать.

За исключением этого (!), Он не сильно отличается от одноплатформенного подхода.

1 голос
/ 10 февраля 2011

Поскольку ваш AppDelegate является делегатом UIApplication - он слушает все уведомления, которые класс UIApplication публикует в течение своего жизненного цикла.didFinishLaunching уведомление является одним из них, и оно заставляет ваш AppDelegate вызвать вышеупомянутый метод.

1 голос
/ 10 февраля 2011

MainWindow.xib определен в вашем info.plist как Main nib file base name.В вашем MainWindow.xib вы определяете первый контроллер, который вы хотите загрузить, в вашем случае RootViewController.

didFinishLaunchingWithOptions: является частью протокола UIApplicationDelegate.Этот метод (в iOS4.0 +) всегда известен как первый, который вызывается при запуске приложения.

...