我试图弄清楚appdelegate,RootViewControoler和UIApplication之间的关系。这是我到目前为止所得到的:
启动应用程序时,main.m会被加载。
从这里开始,您的MainWindow.xib已加载。
在您的MainWindow.xib中,您的文件所有者属于UIApplication类型。
您将UIApplication的委托设置为AppDelegate。
在AppDelegate的源代码中,您可以将RootViewController设置为显示的第一个视图。
这是对的吗?是什么促使AppDelegate最初运行它的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { }
方法?
答案 0 :(得分:44)
当Objective-C应用程序启动时,它首先运行名为main()的函数。它不必在文件“main.m”中,但这就是Xcode向导设置的方式。
在向导生成的main()函数中,有一行:
int retVal = UIApplicationMain(argc, argv, nil, nil);
这就是启动构成整个应用程序的“UIKit”框架的原因。在UIApplicationMain中,创建了UIApplication类型的对象。应用程序启动时UIApplication所做的部分工作是调用UIApplication类的委托成员上的applicationDidFinishLaunchingWithOptions方法。此委托在MainWindow.xib文件中设置为ProjectAppDelegate类的实例,该类是NSObject的子类,符合UIApplicationDelegate协议。
最初会提示AppDelegate 跑吧......
因为在你的MainWindow.xib文件中你连接了(实际上项目向导完成了连接)文件的所有者(它是UIApplication对象)的“委托”出口到.xib文件中的UIApplicationDelegate对象,并且UIApplicationDelegate的类设置为应用程序的UIApplicationDelegate子类。
“MainWindow.xib”并没有什么神奇之处,它可以被称为“Foo.xib”,重要的是Info.plist文件中名为“主nib文件基本名称”的属性是“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文件如何工作以及它们与代码对象的关系的知识。
答案 1 :(得分: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
,则将查找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,并且应用程序就绪,将使用委托方法didFinishLaunchingWithOptions
通知此delegateObject。
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如何以编程方式处理它。在中间使用笔尖并没有太大的不同。
答案 2 :(得分:1)
MainWindow.xib在您的info.plist中定义为 Main nib file base name
。在MainWindow.xib中,您可以定义要加载的第一个控制器RootViewController
。
didFinishLaunchingWithOptions:
是UIApplicationDelegate
协议的一部分。始终知道此方法(在iOS4.0 +中)是启动应用程序时首先调用的方法。
答案 3 :(得分:1)
由于您的AppDelegate
是UIApplication的代理,因此它会监听UIApplication
类在其生命周期中发布的所有通知。 didFinishLaunching
通知就是其中之一,它会导致您AppDelegate
调用上述方法。
答案 4 :(得分:1)
对于Universal - iPhone + iPad - 应用,您可以指定在每个平台上加载不同的NIB,无论是在目标信息面板中,还是在NSMainNibFile~ipad
中添加NSMainNibFile~iphone
和Info.plist
个键。或者,您可以向目标添加MainWindow~ipad.xib
NIB,它将根据Info.plist中的MainWindow.xib
键加载到iPad而不是NSMainNibFile
。
如果您需要对通用应用程序进行更多控制和自定义,则可以手动加载起始NIB。 “Universal”项目模板具有此方法的样板,因此开始使用此技术的最快方法是使用Universal配置文件创建一个新的iOS项目。
在上面的示例中,Main NIB File
在Info.plist
(目标设置)中设置,以便在调用应用程序委托时已经加载了NIB。通常在此设置中,MyAppDelegate
对象也将在NIB中归档(包含一些IBOutlets
),而NIB的File'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]));
}
}
然后检查您的环境和设置,并在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
可以采用您使用应用程序委托的大部分内容 - 这通常是封装应用程序的核心模型类,充当数据源并委托视图和其他东西在NIB。
所以你应该在NIB中有一些root UIView
,它应该连接到view
(File's Owner
)的MYViewController
出口。
请注意,在第一次访问MYViewController.view
属性之前,MYViewController的NIB实际上并未加载。只有这样才会被[MyViewController viewDidLoad]
召唤!最有可能发生这种情况的时间是将其添加到根窗口。
在上面显示的模板代码中,根UIWindow
由应用委托实例化,但没有理由不将它包含在您的NIB中。如果您选择这样做,请小心。如果在此情况下将NIB中窗口的rootViewController
设置为文件所有者,则会在激活窗口时将控制器视图添加到窗口中。在任何情况下都要小心构建第一个NIB。
如果您希望MYViewController管理它,则app委托不一定需要引用您的根UIWindow
,但是将根窗口保留在NIB之外并在其中管理它可能会更清晰应用代表。
除此之外(!)与单平台方法没有太大区别。