========================程序的启动原理====================
一、 项目中的常见文件
** XxxxTest文件夹用来做单元测试。
** Products目录。
* 在Mac程序下,生产的可执行文件app会存放到该目录下。
* iOS程序产生的是ipa文件。这个目录对于iOS程序来说意义不大。
1. Info.plist(全局配置文件, 非常重要, 不能删除 ):
* 在xcode5 中是"项目名称-Info.plist"表示项目的全局配置文件,非常重要。在旧版本xcode中(和xcode6.1),该文件名称就叫Info.plist。
** 注意:自己创建的plist文件中不要包含Info关键字。
* Info.plist中的一些配置项:
1> Bundle display name(在xcode6.1中叫做"Bundle name"), 表示软件安装到手机上后,显示的名称。
** 当修改了该名称后,为了保证有效, 点击Product -> Clean, 同时将软件从模拟器中卸载掉,然后再重新运行。
2> Bundle identifier, app的唯一标识。
3> Bundle version, 每次发布软件的版本号。每次向AppStore上传的同一个软件, 新的版本号必须大于旧的, 否则无法上传。
4> Main storyboard file base name, 对应的就是选中"项目" -> "General" -> "Deployment Info" -> "Main Interface" 中的设置。
5> Supported interface orientations, 标识设备所支持的方向。对应的选中"项目" -> "General" -> "Deployment Info" -> "Device Orientation"。iPhone只支持三种方向, 不支持上下旋转(iPad支持)。Portrait(竖屏)、Landscape Left(横屏向左)、Landscape Right(横屏向右)。
6> Info.plist就是一个xml文件, 用记事本打开看一下。(可扩展性标记语言)
xml: Exrenible Markup language (可扩展性标记语言)
HTML: HyperText Markup language (超文本标记语言)
2. pch文件:(Prefix Header File)(头文件)
* 遇到的问题:
1> 整个项目中很多地方都在使用某个类的头文件。
2> 整个项目中很多地方都在使用同一个"宏"
3> 在项目中很多地方用到了NSLog()函数, 想一下子全都清除掉。
* 解决上面的问题, 可以通过使用PCH文件(Prefix Header File)。
* pch文件就是一个头文件(类似于*.h文件)。
** 注意: PCH文件的特点, 项目中的所有其他代码文件无需显示导入该PCH文件, 默认就都可以访问(其他文件无需手动#import该 pch文件就能使用)。
* 主要作用:
1> 可以放一些公用的宏定义。
2> 把公共的Model类的#import导入写到pch文件中。
3> 自定义NSLog()。例如: #define CZLog(...) NSLog(__VA_ARGS__)
** 遇到的问题: 在项目中很多地方用到了NSLog()函数, 想一下子全都清除掉。
* 在xcode6以后, 默认没有创建pch文件。
** 需要自己新建一个
** 创建方式: 选择"Supporting Files" -> 右键 -> "New File" -> "Other" -> "PCH File" -> "PrefixHeader.pch"。
* 在该文件中定义如下宏:
** #define ABC 10
* 选中项目 -> Build Setting -> All -> 搜索"prefix head" -> 修改Prefix Header的内容为:
** "$(SRCROOT)/$(PRODUCT_NAME)/PrefixHeader.pch" (如果有问题,换下面的方式,可能会与中文有关)
** 或者
** "$(SRCROOT)/对应的文件夹名/PrefixHeader.pch"
** 参考连接: http://www.cnblogs.com/YouXianMing/p/3989155.html
3. UIApplication对象介绍:
1> 一个UIApplication代表是一个应用程序,而且是单例的。
** 用来封装整个应用程序的一个对象, 比如当应用程序执行到某个时期要做什么, 生命周期等。
2> 获取UIApplication对象: [UIApplication sharedApplication];(单例的)
3> 当一个iOS程序启动后,首先创建的第一个对象就是UIApplication对象。
4> 利用UIApplication可以做一些应用级别的操作。
* 应用级别操作:
1> QQ有消息的时候右上角的消息条数。
// 获取UIApplication对象。
UIApplication *app = [UIApplication sharedApplication];
// 设置右上角, 有10条消息
app.applicationIconBadgeNumber = 10;
// 取消显示消息
app.applicationIconBadgeNumber = 0;
2> 联网操作时,状态栏上的等待图标指示器。waiting图标。
UIApplication *app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
3> 利用UIApplication打开某个资源:
** 系统会自动根据协议识别使用某个app打开。
UIApplication *app = [UIApplication sharedApplication];
** 打开一个网页:
[app openURL:[NSURL URLWithString:@"http://ios.icast.cn"]];
** 打电话
[app openURL:[NSURL URLWithString:@"tel://10086"]];
** 发短信
[app openURL:[NSURL URLWithString:@"sms://10086"]];
** 发邮件
[app openURL:[NSURL URLWithString:@"mailto://12345@qq.com"]];
** 使用openURL方法也可以打开其他应用,在不同应用之间互相调用对方。
** 美图秀秀, 点击分享到"新浪微博", 打开"新浪微博"选择账号, 跳转回"美图秀秀", 开始分享
** 喜马拉雅, 使用微博、QQ 账号 登录。都需要应用程序间跳转。
5. UIApplicationDelegate介绍。
** 新建完项目以后的那个AppDelegate文件, 就是UIApplication的代理对象。
* 并且该代理对象已经被设置好了, 无需我们手动设置了。
* 在main函数中进行的设置
** App容易受到干扰。正在玩游戏,一个电话打过来了。
* 应用程序的生命周期事件(如程序启动和关闭)
* 系统事件(如来电)
* 内存警告
* … …
** 处理这些干扰事件,就要用到AppDelegate代理对象了。
** 总结: AppDelegate的主要作用就是处理(监听)应用程序本身的各种事件:
* 应用程序启动完毕
* 应用程序进入后台
* 应用程序进入前台
* 内存警告
* 等等, 都是应用程序自身的一些事件
** 要想成为UIApplication的代理对象, 必须遵守:UIApplicationDelegate协议。
** 代理中的若干方法介绍:
//app启动完成后执行
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions
{
NSLog(@"didFinishLaunchingWithOptions");
return YES;
}
//app即将推出活动状态的时候调用(失去焦点,不可交互) //此方法在来电或来短信的时候被调用
//
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
NSLog(@"applicationWillResignActive");
}
//app进入后台的时候调用
//在此方法中保存应用程序的数据和状态
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog(@"applicationDidEnterBackground");
}
//app即将进入前台的时候调用
//在此方法中还原应用程序的数据和状态
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
NSLog(@"applicationWillEnterForeground");
}
//app获得焦点的时候调用(能够和用户交互)
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was
//inactive. If the application was previously in the background, optionally refresh the user interface.
NSLog(@"applicationDidBecomeActive");
}
//app将被销毁
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also
//applicationDidEnterBackground:.
NSLog(@"applicationWillTerminate");
}
//app收到系统的内存警告,清除不必要的内存
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
NSLog(@"applicationDidReceiveMemoryWarning");
}
6. UIApplicationMain函数介绍。
7. iOS程序启动过程。参考PPT上的图片。
1> 打开程序。
2> 调用main函数。
3> 在main函数中调用: UIApplicationMain()函数。
* 在UIApplicationMain()函数中:
1. 创建UIApplication对象
2. 创建AppDelegate代理对象
3. 将AppDelegate代理对象设置给UIApplication对象。
4. 在UIApplicationMain()函数开启一个"死循环(事件循环)", 所以程序不会退出, 我们可以任意使用。在这个"死循环(事件循环)"中程序不断监听用户的各种事件, 依次处理(依靠"事件队列"实现)。
5. 程序启动完毕: 触发application:didFinishLaunchingWithOptions事件。
在此方法中创建了application对象和它的代理对象,并设置了application的代理对象,开启消息循环。在消息循环中不断监测 消息队列 中是否有新的事件(队列是先进先出)(事件队列的目的是为了提高性能) (消息循环可以简单理解为一个死循环,当系统有事件被触发,事件所执行的方法会放入消息队列中,消息循环的作用就是不停的去消息 队列中看有没有新的消息,有就执行,用户感觉不到等待的时间)
//先检查info.plist文件中是否有principalClassName键,如果没有的话UIApplicationMain中的principalClassName参 数,会默认被指定为UIApplication ,代理类的会被初始化
4> 程序退出。
8. UIWindow对象。
** UIWindow是一种特殊的UIView, UIWindow也是继承自UIView。
** 通常一个app只会有一个UIWindow对象。
** iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的view,最后将控制器的view添加到UIWindow上,于是控制器的view就显示在屏幕上了
** 一个iOS程序之所以能显示到屏幕上,完全是因为它有UIWindow
** 在文档中找: Cocoa Touch Layer -> UIKit -> Guides -> View Controller Programming Guide for iOS -> View Controller Basics -> 关于UIWindow与控制器View的关系图片。
** 创建过程UIWindow -> UIViewController -> UIView -> 把UIView加到UIWindow对象中。
** 通过storyboard方式启动项目的方式:
1> 新建一个Single View Application, 观察AppDelegate中的代码, 虽然有application: didFinishLaunchingWithOptions:方法, 但是并没有在该方法中创建任何UIWindow。
2> 带Main.storyboard的启动方式:
1. 启动App
3. 创建UIWindow对象。在该方法中系统已经生成了创建UIWindow的方法, 不需要我们手动编写。
4. 根据Info.plist文件配置(Main Interface),找到需要加载的storyboard文件(Main.storyboard)
5. 找到Main.storyboard中的Is Initial View Controller 对应的控制器类, 创建该控制器对象。
6. 根据storyboard中的配置, 创建控制器对应的view。
7. 设置UIWindow的根控制器(rootViewController)为刚才创建的控制器。
调用AppDelegate的 application: didFinishLaunchingWithOptions:方法
8. 显示UIWindow([self.window makeKeyAndVisible])。
9. 注意, 当删除了Main Interface 中的配置后, 在运行程序会看到“黑屏”, 因为没有创建"控制器", 也没有设置UIWindow的rootViewController。
================== 演示在一个空项目中创建界面内容 ========================
** xcode6以后没有创建空项目的模板, 所以可以新建一个Single View Application, 然后删除Main.storyboard 、 默认的ViewController、 同时设置Main Interface为空。然后运行, "黑屏"。因为没有UIWindow, 就没有界面。
** 开始一步一步创建界面:
1> 在application: didFinishLaunchingWithOptions:方法中创建UIWindow对象。
2> 新建一个自己的控制器类, 并在该控制的viewDidLoad事件中, 设置控制器的view的背景色为红色。
3> 将控制器的view添加到UIWindow中。
**通过设置UIWindow的根控制器的方式
** 思考: 如果不使用控制器, 直接把控件添加到UIWindow中行不行?
** 总结程序启动完整过程。(有storyboard和没有storyboard的分别总结)
一、 有storyboard文件
1. 调用main函数。
2. 调用UIApplicationMain函数。
3. 创建UIApplication对象 、 AppDelegate对象
4. 设置UIApplicatio对象的代理是AppDelegate对象。
5. AppDelegate对象开始监听"系统事件(应用程序的事件)",进入"事件循环"
6. 程序启动完毕后调用 application: didFinishLaunchingWithOptions:方法。
7. 在application: didFinishLaunchingWithOptions:方法中自动创建:
* UIWindow对象。
* 根据Info.plist文件配置(Main Interface),找到需要加载的storyboard文件(Main.storyboard)
* 找到Main.storyboard中的Is Initial View Controller 对应的控制器类, 创建该控制器对象。
* 根据storyboard中的配置, 创建控制器对应的view。
* 设置UIWindow的根控制器(rootViewController)为刚才创建的控制器。
* 显示UIWindow([self.window makeKeyAndVisible])。
二、 没有storyboard文件
1. 调用main函数。
2. 调用UIApplicationMain函数。
3. 创建UIApplication对象 、 AppDelegate对象
4. 设置UIApplicatio对象的代理是AppDelegate对象。
5. AppDelegate对象开始监听"系统事件(应用程序的事件)",进入"事件循环"。
6. 程序启动完毕后调用 application: didFinishLaunchingWithOptions:方法。
7. 在application: didFinishLaunchingWithOptions:方法中手动创建:
* UIWindow
* 控制器
* 设置UIWindow的根控制器是刚才创建的控制器
* 显示UIWindow
*** 总结:UIApplication(应用程序对象) 、 AppDelegate(应用程序代理对象) 、 UIWindow(窗口对象) 、 ViewController(控制器对象)四个对象的关系。
*** 注意:要让设计器中看到的与最终手机屏幕上看到的一致,则把设计器中的ViewController的大小调整到与手机模拟器大小一致了。否则可能会出现在设计器中看到的与模拟器中运行的结果不一致问题。
*** 注意:在xcode帮助文档中查看iOS7 和 iOS6中控件的不同展示方式:iOS8.1 -> User Experience -> Guides -> iOS 7 UI Transition Guide, 然后打开右侧的子菜单。