如果我们使用模板来新建一个project,比如Master Detail Application或者tabbed Application之类,那么Xcode将会为我们自动生成Main.storyboard文件,这样一来那些本来实在源代码中间中被定义的线程执行流程,现在全部在这个故事版文件中被定义了。用故事版当然有故事版的好处,只能用来定义的可视化元素现在允许程序员用可视化的方式来定义,不过对于并不是非常熟悉iOS编程的程序员来说,这种搭建UI的方式并不利于维护修改,因为程序员不容易看到线程的实际执行过程。
如果用代码的方式,程序员就可以看到什么时候线程创建了UIWindow的实例,并定义这个窗口要承载的界面元素,以及什么时候根据UIWindow实例中的代码和数据绘出图形。方法makeKeyAndVisible告诉了线程应该如何绘图。
主线程执行流程
iOS应用主线程执行流程如下图,方块代表了代码集合或者线程需要获得的其它资源,箭头方向代表了线程的转移方向。
上图就是主线程的声明周期。我们看看main函数:
#import <UIKit/UIKit.h> #import "QSAppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
这是线程的入口,之后执行函数UIApplicationMain.
UIApplicationMain()
函数是初始化程序的核心,它接受4个参数。
int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName );
argc和argv:
来自于main()接受的两个参数;
第三个参数:
主要类的名字(principal class name),必须是UIApplication或其子类的名字,这个类的一个实例引用了当前iPhone应用,定义了事件循环的代码。它会告诉线程去读info.plist文件获取配置信息,以及Main.storyboard;如果这个参数为nil,则默认为@"UIApplication";
第四个参数:
代理类的名字(delegate class name),线程在内存中创建一个代理类的实例,定义一些处理与操作系统相关的事件的代码。
UIApplication类(由UIKit来定义)
UIApplication(或者其子类)的对象主要做下面几件事:
1)负责处理到来的用户事件,并分发事件消息到监听该消息的目标对象(sender, action)。
2)管理以及控制视图,包括呈现、控制行为、当前显示视图等。
3)该对象对应一个应用程序委托对象,当一些生命周期内重要事件(可以包括系统事件或者生命周期控制事件)发生时,应用程序通知该对象。例如,应用程序启动、内存不够了或者应用程序结束等,让这些事件发生时,应用程序委托去响应。
这个类中定义了很多有用的代码,可供线程执行。
1.设置icon上的数字图标
//设置主界面icon上的数字图标,在2.0中引进, 缺省为0
[UIApplication sharedApplication].applicationIconBadgeNumber = 4;
2.设置摇动手势的时候,是否支持redo,undo操作
//摇动手势,是否支持redo undo操作。3.0以后引进,缺省YES
[UIApplication sharedApplication].applicationSupportsShakeToEdit =YES;
3.判断程序运行状态
//判断程序运行状态,在2.0以后引入
if([UIApplication sharedApplication].applicationState ==UIApplicationStateInactive){
NSLog(@"程序在运行状态");
}
4.阻止屏幕变暗进入休眠状态
//阻止屏幕变暗,慎重使用,缺省为no 2.0
[UIApplication sharedApplication].idleTimerDisabled =YES;
慎重使用本功能,因为非常耗电。
5.显示联网状态
//显示联网标记 2.0
[UIApplication sharedApplication].networkActivityIndicatorVisible =YES;
6.在map上显示一个地址
NSString* addressText [email protected]"1 Infinite Loop, Cupertino, CA 95014";
// URL encode the spaces
addressText = [addressText stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSString* urlText = [NSString stringWithFormat:@"http://maps.google.com/maps?q=%@", addressText];
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:urlText]];
7.发送电子邮件
NSString *recipients [email protected]"mailto:[email protected][email protected],[email protected]&subject=Hello from California!";
NSString *body [email protected]"&body=It is raining in sunny California!";
NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body];
email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:email]];
8.打电话到一个号码
// Call Google 411
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"tel://8004664411"]];
9.发送短信
// Text to Google SMS
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"sms://466453"]];
10.打开一个网址
// Lanuch any iPhone developers fav site
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"http://itunesconnect.apple.com"]];
AppDelegate类(由程序员来定义)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
当由于其它方法打开应用程序(如URL指定或者连接),通知委托启动完毕
- (void)applicationWillTerminate:(UIApplication *)application
通知委托,应用程序将在关闭 退出,请做一些清理工作。
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
通知委托,应用程序收到了为来自系统的内存不足警告。-(void)applicationSignificantTimeChange:(UIApplication *)application
通知委托系统时间发生改变(主要是指时间属性,而不是具体的时间值)
打开URL
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
打开指定的URL
控制状态栏方位变化
– application:willChangeStatusBarOrientation:duration:
设备方向将要发生改变
– application:didChangeStatusBarOrientation:
活动状态改变
- (void)applicationWillResignActive:(UIApplication *)application
通知委托应用程序将进入非活动状态,在此期间,应用程序不接收消息或事件。-(void)applicationDidBecomeActive:(UIApplication *)application
通知委托应用程序进入活动状态,请恢复数据
可以看到UIApplication的头文件实现
@interface UIApplication :UIResponder <UIActionSheetDelegate>{
@package
id<UIApplicationDelegate> _delegate ; //这就是应用程序委托。
NSTimer .......
}
因此,如果我们希望获得appdelegate实例,不必通过import类的方式,只需要:
UIApplicationDelegate* delegate = [[UIApplication sharedApplication] delegate];
用代码替换故事版
说了很多iOS程序执行的真正流程,我们来检验一下我们的理解吧。
假如你的工程类都是以TC开头。
1.import你的appdelegate类,并修改第四个参数如下:
UIApplicationMain(argc, argv, nil, NSStringFromClass([TCAppDelegate class]));
2.删除Main.storyboard文件
3.在工程的Info.plist文件中删除Main storyboard file base name
4、修改delegate类中定义的方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; UIViewController *webPageViewController = [[WebPageViewController alloc] init]; UIViewController *personalViewController = [[PersonalViewController alloc]init]; UIViewController *meViewController = [[MeViewController alloc]init]; UINavigationController *personalNavigationController = [[UINavigationController alloc]init]; personalNavigationController.viewControllers = [NSArray arrayWithObjects:personalViewController, nil]; UINavigationController *meNavigationController = [[UINavigationController alloc]init]; meNavigationController.viewControllers = [NSArray arrayWithObjects:meViewController, nil]; _tabBarController = [[UITabBarController alloc]init]; _tabBarController.viewControllers = [NSArray arrayWithObjects:webPageViewController, personalNavigationController, meNavigationController,nil]; self.window.rootViewController = _tabBarController; _tabBarController.selectedIndex = 0; self.window.backgroundColor = [UIColor whiteColor]; [self customizeAppearance]; [self.window makeKeyAndVisible]; return YES; }
这样就避免了根据故事版文件的信息来加载窗口以及它所承载的界面元素。