25

我试图弄清楚 appdelegate、RootViewControoler 和 UIApplication 之间的关系。到目前为止,这是我想出的:

启动应用程序时,会加载 main.m。

从这里,您的 MainWindow.xib 被加载。

在您的 MainWindow.xib 中,您的文件所有者属于 UIApplication 类型。

您将 UIApplication 的委托设置为您的 AppDelegate。

在 AppDelegate 的源代码中,您可以将 RootViewController 设置为显示的第一个视图。

这是正确的吗?是什么促使 AppDelegate 最初运行它

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

方法?

4

5 回答 5

45

当一个 Objective-C 应用程序启动时,它首先运行名为 main() 的函数。它不必在文件“main.m”中,但这就是 Xcode 向导设置的方式。

在向导生成的 main() 函数中,有这一行:

int retVal = UIApplicationMain(argc, argv, nil, nil);

这就是构成整个应用程序的“UIKit”框架的开始。在 UIApplicationMain 中,创建了一个 UIApplication 类型的对象。UIApplication 在应用程序启动时所做的部分工作是在 UIApplication 类的委托成员上调用 applicationDidFinishLaunchingWithOptions 方法。此委托在 MainWindow.xib 文件中设置为您的 ProjectAppDelegate 类的实例,该类是符合 UIApplicationDelegate 协议的 NSObject 的子类。

是什么促使 AppDelegate 最初运行它...

因为在您的 MainWindow.xib 文件中,您已经连接(项目向导实际上进行了连接)文件所有者(即 UIApplication 对象)的“委托”出口到 .xib 文件中的 UIApplicationDelegate 对象,以及类UIApplicationDelegate 的设置为您应用的 UIApplicationDelegate 子类。

“MainWindow.xib”并没有什么神奇之处,它可以称为“Foo.xib”,重要的是Info.plist文件中名为“Main nib file base name”的属性是“MainWindow”。尝试将 MainWindow.xib 重命名为 Foo.xib 并将 Info.plist 中的“主 nib 文件基本名称”更改为“Foo”,您会看到它仍然有效。

编辑:更多关于 RootController

同样,所谓的“RootController”并没有什么神奇之处。这只是 Xcode 新项目向导为您创建的 UIViewController 子类的名称。

该向导将两个类的代码放置在项目中: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 文件的工作原理以及它们与代码对象的关系。

于 2011-02-10T06:43:08.647 回答
15

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,那么将在主窗口 xib 中查找 Info.plist(通常是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可以是字符串UIApplicationUIApplication. 同样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:...];

如果不使用 nib,这就是 UIApplication 以编程方式处理它的方式。在中间使用笔尖并没有太大的不同。

于 2011-02-10T06:49:18.820 回答
1

对于通用(iPhone + iPad)应用程序,您可以指定在每个平台上加载不同的 NIB,或者在目标信息面板中,或者通过将NSMainNibFile~ipadNSMainNibFile~iphone键添加到您的Info.plist. 或者,您可以将MainWindow~ipad.xibNIB 添加到您的目标,它将MainWindow.xib根据NSMainNibFileInfo.plist 中的键加载到 iPad 而不是 .

如果您需要对通用应用程序进行更多控制和自定义,您可以手动加载启动 NIB。“通用”项目模板具有此方法的样板,因此开始使用此技术的最快方法是使用通用配置文件创建一个新的 iOS 项目。

在上面的示例中,Main NIB FileInfo.plist(目标设置)中进行了设置,以便在调用应用程序委托时已经加载了一个 NIB。通常在此设置中,MyAppDelegate对象也将存档在 NIB 中(带有一些IBOutlets),并且 NIBFile's Owner将设置为UIApplication.

对于能够容纳两种替代布局的通用项目,主 NIB 文件键被排除在Info.plist. 然后它以编程方式实例化应用程序委托对象UIApplicationMain

#import "MYAppDelegate.h"

int main(int argc, char *argv[])
{
  @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([MYAppDelegate class]));
  }
}

然后检查您的环境和设置并在其中加载适当的NIBapplication: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可以采用您可能一直在使用您的应用程序委托的大部分内容 - 这通常是封装应用程序的核心模型类,充当数据源并委托 NIB 中的视图和其他内容。

所以你UIView应该在NIB中有一些根,它应该连接到( )的view出口。File's OwnerMYViewController

MYViewController.view请注意,MYViewController 的 NIB 直到第一次访问该属性时才真正加载。只有这样才会[MyViewController viewDidLoad]被调用!最有可能发生这种情况的时间是您将其添加到根窗口时。

在上面显示的模板代码中,根由UIWindow应用程序委托实例化,但没有理由不能将它包含在您的 NIB 中。如果您选择这样做,请小心。如果rootViewController在这种情况下将 NIB 中的窗口的所有者设置为文件的所有者,则会导致在窗口被激活时将控制器的视图添加到窗口中。在任何情况下都要小心构建第一个 NIB。

UIWindow如果您希望 MYViewController 管理它,应用程序委托不一定需要对您的根目录的引用,但总体而言,将根窗口保持在您的 NIB 之外并在应用程序委托中管理它可能会更干净。

除此之外(!)与单平台方法没有太大区别。

于 2012-08-19T22:34:44.467 回答
1

MainWindow.xib 在 info.plist 中定义为Main nib file base name. 在您的 MainWindow.xib 中,您定义要加载的第一个控制器,在您的情况下,RootViewController.

didFinishLaunchingWithOptions:UIApplicationDelegate协议的一部分。这个方法(在 iOS4.0+ 中)总是被认为是在启动应用程序时最先被调用的。

于 2011-02-10T06:41:23.890 回答
1

由于您AppDelegate是 UIApplication 的代表 - 它会UIApplication在其生命周期内监听类发布的所有通知。didFinishLaunching通知就是其中之一,它会导致您AppDelegate调用上述方法。

于 2011-02-10T06:41:50.723 回答