iOS开发2-Xcode介绍
目标
还是用HelloWorld项目来介绍我们的Xcode开发工具,最最重要的还是介绍一下程序的运行过程。
内容
1.Xcode
Xcode窗口布局一览:
工具栏:位于程序窗口最顶端,上面的很多按钮,可以每个试一试。
导航器面板:位于窗口左边,通常用来显示项目中的文件列表,也可浏览其他内容。
编辑器面板:位于中间位置,在这里疯狂写代码。
检查器面板:位于窗口右边,显示的是与选中内容有关的信息,以及修改的属性值等。
调试器面板:位于窗口底部,调试器运行的时候,堆栈和调试控制器会出现在这里。
库面板:在窗口的右下角,列有项目资源,对象,代码片段和其他会用到的东西。
1.工具栏
1)活动视图:(activity view)工具栏中间的大方框就是的。顾名思义,活动视图就是显示当前正在进行的操作或处理。
2)标准编辑器:(standard editor)提供一个面板,用于编辑文件或编辑项目相关的配置数值。
3)辅助编辑器:(assistant editor)它将编辑面板分割为左右两个部分。界面构建和视图控制器关联时常用到。
4)版本编辑器:(version editor)会出现一个对比视图,这个对比视图可以跟Subversion和git等版本控制系统协同工作。
编辑器按钮的右侧是另一组开关,用于控制左侧的面板、右侧编辑器视图以及项目窗口底部的调试区域的显示与隐藏。关于这些按钮,点两下就应该知道是做什么的了。
2.导航视图
1)项目导航面板(project navigator)
这个面板列出了项目用到的所有文件。
2)符号导航面板(symbol navigator)
顾名思义,编译器能识别的符号都在这,包含OC结构体、枚举类型、全局变量等。
3)搜索导航面板(Find navigator)
可以对工作区中的所有文件执行搜索,Find(查询)功能也可以改成Replace(替换)功能。
4)问题导航面板(issues navigator)
构建项目过程中出现的任何错误或警告都可以在这里看到。
点击错误,就会跳转到编辑器面板中相应的代码行。
5)测试导航面板(Test navigator)
如果正在使用Xcode内置的单元测试功能,这里就可以看到测试结果。
6)调试导航面板(debug navigator)
这里是观察调试过程的主要区域。
7)断点导航面板(breakpoint navigator)
可以在这里看到所有已设置的所有断点,并可以对断点进行操作。
8)日志导航面板(log navigator)
这里保存着构建结果和运行日志的历史记录。
3.跳转栏
在编辑器的顶部,你会找到一个叫作跳转栏(jump bar)的特殊控件,你只需要单击一次,就能跳转到当前导航层次结构中的特定元素。
主要用于页面函数浏览、查找。
4.界面构建器(Interface Builder)
1)界面文件格式
界面构建器支持不同的文件类型:最初的版本使用扩展名为nib的二进制格式,后来改用扩展名为xib,这是一种基于XML的衍生格式 。
两者包含了完全一样的文档内容,但是xib格式的版本却是基于文本格式的,这样做有很多优势,尤其是在你使用版本控制系统的时候。
一个nib文件可以包含任意数量的对象。但在iOS项目中,通常每个nib文件负责包含一个视图(一般都是全屏视图)以及相关联的控制器或对象。这种方式只在应用需要某个视图时,才会加载他对应的nib文件。这样做的目的是为了内存受限的iOS设备节约运行时的内存。
最近几年,界面构建器开始支持另一种新的文件类型,即storyboard(故事板,分镜)。
2)Storyboard(故事板)
一个storyboard可以包含多个视图和控制器,以及如何在应用运行时进行相互连接的配置信息。
与一次就加载完所有的内容的nib文件不同,storyboard不会一次加载所有的内容,而是在你需要加载某视图和控制器时才向它请求特定的内容。
视图:就是你眼睛能看到并可以在界面构建器中进行编辑的部分,
控制器:则是你编写的代码,用来 处理用户的交互事件。
应用的实际操作其实都是在控制器内执行的。
视图顶部有:
View Controller(视图控制器)、First Responder(第一响应者)、Exit(离开)。
视图控制器:代表会从某存储文件中加载控制器以及相关联的视图。
第一响应者:就是当前用户正在进行交互的对象。如果用户正在向一个文本框中输入数据,那么这个文本框就是当前的First Responder。
UIView
在这里我们需要对上面的代码一样解释,在弄清上面的代码之前我们不得不熟悉一个UIKit中最重要的类UIView。
1. UIView就是指界面可见的控件元素,所有的控件最终都继承自UIView,UIView中还可以添加其他UIView(通过addSubView方法);
2. 在一个iOS应用中必须有一个主窗口UIWindow(理论上也可以有多个UIWindow但是只有一个是主Window,而且只有主Window可以和用户交互),UIWindow也是继承自UIView,它拥有UIView的所有属性、方法;
3. 在UIWindow中必须有一个根控制器,这个控制器距离UIWindow是最近的;设置一个控制器为根控制器和直接通过addSubView添加控制器的视图(view属性)到window并不完全一样(例如如果仅仅添加控制器视图那么应用虽然可以显示但是不支持旋转);
4. UIViewController是视图控制器,主要用来控制UIView,在UIViewController内部有一个UIView(view属性);
在上面的代码中我们首先在应用程序加载完毕之后加载一个UIWindow对象,同时把我们的视图控制器KCMainController设置为UIWindow的根视图控制器,然后设置这个UIWindow为主窗口并可见。当主窗口设置为可见过程中会调用视图控制器的loadView方法来加载视图(注意视图控制器的loadView方法是延迟加载的,第一次调用视图控制器的view属性才会调用此方法;由于makeKeyAndVisible方法中会使用视图控制器的view属性所以此时会调用视图控制器的loadView方法),视图加载完之后调用viewDidLoad方法,在这个方法中我们添加登录相关控件并将这些控件加载到视图控制器KCMainViewController的视图view中。
下面我们看一下应用程序最终的布局,相信通过这张图大家对于iOS的布局会有一个大致了解:
3)库面板
窗口的右下角位置就是库面板了。
库是可重用对象的集合,可以在自己的程序中使用它们。
文件模板库(file template library):包含一些文件模板,向项目中添加新文件时可以使用它们。
代码片段库(code snippet library):包含一些精选的代码片段,可以直接拖放使用。
对象库(object library):包含各种可重用对象,比如文本框、标签、滑块、按钮等。
媒体库(media library):包括用户的所有媒体文件,有图片、声音和影片文件等。
4)属性修改
属性面板中列出当前选中对象的属性值,可以在这个面板上进行属性修改。
2.程序运行过程
在几乎所有的程序开发中程序一般都是从main函数开始运行的,那么IOS程序也不例外,在上图中我们可以看到Xcode为我们生成了一个main.m文件:
//
// main.m
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
这个默认的iOS程序就是从main函数开始执行的,但是在main函数中我们其实只能看到一个方法,这个方法内部是一个消息循环(相当于一个死循环),因此运行到这个方法UIApplicationMain之后程序不会自动退出,而只有当用户手动关闭程序这个循环才结束。这个方法有四个参数:
• 第一个参数和第二个参数其实就是main函数的参数,分别代表:参数个数、参数内容;
• 第三个参数代表UIApplication类(或子类)字符串,这个参数默认为nil则代表默认为UIApplication类,用户可以自定义一个类继承于这个类;如果为nil则等价于NSStringFromClass([UIApplication class]),大家可以自己试验,效果完全一样;UIApplication是单例模式,一个应用程序只有一个UIApplication对象或子对象;
• 第四个参数是UIApplication的代理类字符串,默认生成的是AppDelegate类,这个类主要用于监听整个应用程序生命周期的各个事件(其实类似于之前我们文章中提到的事件监听代理),当UIApplication运行过程中引发了某个事件之后会调用代理中对应的方法;
小技巧:
其实在Xcode中如果要看一些系统方法的解释或者参数说明,可以直接鼠标放到这个方法上,在Xcode右侧面板中就会给出帮助提示,如下图当我们放到UIApplicationMain上之后:
也就是说当执行UIApplicationMain方法后这个方法会根据第三个参数创建对应的UIApplication对象,这个对象会根据第四个参数AppDelegate创建并指定此对象为UIApplication的代理;同时UIApplication会开启一个消息循环不断监听应用程序的各个活动,当应用程序生命周期发生改变UIApplication就会调用代理对应的方法。
既然应用程序UIApplication是通过代理和外部交互的,那么我们就有必要清楚AppDelegate的操作细节,下面是UIApplication详细的代码:
AppDelegate.h
//
// AppDelegate.h
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
AppDelegate.m
//
// AppDelegate.m
//
#import "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
@end
这个类中定义了应用程序生命周期中各个事件的执行方法:
- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions;
程序启动之后执行,只有在第一次程序启动后才执行,以后不再执行;
(void)applicationWillResignActive:(UIApplication *)application;
程序将要被激活时(获得焦点)执行,程序激活用户才能操作;(void)applicationDidEnterBackground:(UIApplication *)application;
程序进入后台后执行,注意进入后台时会先失去焦点再进入后台;(void)applicationWillEnterForeground:(UIApplication *)application;
程序将要进入前台时执行;(void)applicationDidBecomeActive:(UIApplication *)application;
程序被激活(获得焦点)后执行,注意程序被激活时会先进入前台再被激活;(void)applicationWillTerminate:(UIApplication *)application;
程序在终止时执行,包括正常终止或异常终止,例如说一个应用程序在后太运行(例如音乐播放软件、社交软件等)占用太多内存这时会意外终止调用此方法;
为了演示程序的生命周期,不妨在每个事件中都输出一段内容,简单调整上面的代码:
AppDelegate.m
//
// AppDelegate.m
//
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(@"程序已经启动...");
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(@"程序将要失去焦点...");
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"程序已经进入后台...");
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(@"程序将要进入前台...");
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(@"程序已经获得焦点...");
}
- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(@"程序将要终止...");
}
-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application{
}
@end
下面是各个不同操作的运行结果:
相信通过上面运行过程大家会对整个运行周期有个大概了解。
比较容易混淆的地方就是应用程序进入前台、激活、失去焦点、进入后台,这几个方法大家要清楚。
如果一个应用程序失去焦点那么意味着用户当前无法进行交互操作,因此一般会先失去焦点再进入后台防止进入后台过程中用户误操作;
如果一个应用程序进入前台也是类似的,会先进入前台再获得焦点,这样进入前台过程中未完全准备好的情况下用户无法操作。另外一般如果应用程序要保存用户数据会在注销激活中进行(而不是在进入后台方法中进行),因为如果用户双击Home不会进入后台只会注销激活;
如果用户恢复应用状态一般在进入激活状态时处理(而不是在进入前台方法中进行),因为用户可能是从任务栏直接返回应用,此时不会执行进入前台操作。
当然,上面的事件并不是所有AppDelegate事件,而是最常用的一些事件,其他事件大家可以查阅官方文档,例如-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application;用于在内存占用过多发出内存警告时调用并通知对应的ViewController调用其内存回收方法。
这里简单以图形方式描述一下应用程序的调用过程:
小结
- 了解Xcode的各个面板,说熟悉的话还得等每天应用,自然会熟悉,注意使用快捷键,提高开发效率,也让人感觉很叼的样子,当然,只是样子而已,哈哈。
- 理解程序的运行过程,理解程序运行周期。