一、前文回顾
在之前已经介绍了 iOS的学习路线图,因为中间遇到一些Android开发问题,所以就耽搁了一段时间,那么接下来的这段时间我们将继续开始iOS的狂暴之路学习,按照国际惯例,第一个应用当然是我们的HelloWorld程序了。那么本文将会通过这么一个简单的程序来讲解一下iOS中的程序生命周期,应用中关键的几个对象,项目结构,最后在手把手的创建一个空项目。
二、建立简单程序HelloWorld
下面先用Xcode来新建一个HelloWorld程序:
点击下一步即可:
这里和我们在AndroidStudio中新建Android程序非常类似,不多说了,点击下一步即可:
这样项目就建立好了,下面我们让程序运行起来,展示一个HelloWorld文字。这个有两种方式,在Android中也是类似,我们这里使用UILabel来进行操作。类似于Android中的TextView控件,一种方式是代码写布局,一种是采用拖的方式类似于Android中的布局文件。这里为了方便而且以后会详细介绍拖的那种方式,直接用代码进行编写了。
那么我们在哪里编写代码呢?这里的入口是在根控制器ViewController类:
这个入口类似于Android中的Activity的onCreate方法,我们一般都是在这里初始化一个View,这里看到代码逻辑不是很复杂,但是隐含的知识点很多,不过不是本文的重点,比如这里涉及到了iOS的坐标问题,View之间的关系等,这个是在后面章节会详细介绍View知识点在加以分析。代码写完了,运行程序吧,运行也很简单:
直接点击左上角的运行按钮即可,同时可以选择项目运行的目标设备。运行结果如下:
这里看到了,在4s设备中一个简单的HelloWorld就展示出来了。好了,下面就要开始我们今天的重要内容了,我们从这个简单的程序入手能够学习到哪些知识。
三、程序知识点分析
第一、程序的入口和生命周期
我们在开发Android程序的时候都知道其实一个应用程序的入口并不是MainActivity的onCreate方法也不是Application的onCreate方法,而是ActivityThread的main方法,与之类似,在iOS中一个程序的入口也是main方法,而这个main类在main方法中:
每个程序都有一个main.m这个类,内部有一个main方法,而这个方法我们看到和C语言中的main函数形式是一致的,入口就在这里,那么这里干了一件事就是托付应用程序的代理对象AppDelegate类,也就是把整个应用程序的逻辑都托付给了AppDelegate类,在iOS中这种方式叫做代理,而在Android中我们通常叫做回调机制,然后UIApplicationMain类就会和AppDelegate类进行交互,比如应用的生命周期,事件处理等,那么下面就来看一下AppDelegate这个类:
在这个类中我们可以看到很多时机的回调方法,这个就是和应用的生命周期方法相关联的,下面就来一一分析这些方法的回调时机:
1、告诉代理启动基本完成程序准备开始运行
– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
2、告诉代理进程启动但还没进入状态保存
– (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
3、当应用程序将要入非活动状态执行,在此期间,应用程序不接收消息或事件,比如来电话了
– (void)applicationWillResignActive:(UIApplication *)application
4、当程序被推送到后台的时候调用。所以要设置后台继续运行,则在这个函数里面设置即可
– (void)applicationDidEnterBackground:(UIApplication *)application
5、当程序从后台将要重新回到前台时候调用,这个刚好跟上面的那个方法相反
– (void)applicationWillEnterForeground:(UIApplication *)application
6、当应用程序入活动状态执行,这个刚好跟上面那个方法相反
– (void)applicationDidBecomeActive:(UIApplication *)application
7、当程序将要退出是被调用,通常是用来保存数据和一些退出前的清理工作
– (void)applicationWillTerminate:(UIApplication *)application
8、当应用接收到内存警告回调方法
-(void)applicationDidReceiveMemoryWarning:(UIApplication *)application
看完上面的这些生命周期的回调方法之后,大家可能会想到这个和Android中的Activity非常类似了,而不是Application类了,在Android中比较复杂一般有三个物理按键关系Activity的生命周期,而在iOS中只有一个Home键,所以这里的生命周期很好理解的。
第二、应用的窗口
上面就介绍完了iOS程序的生命周期相关的类AppDelegate,下面还需要继续分析,怎么关联上ViewController类的,把HelloWorld展示出来的?
首先我们在程序启动完成之后的回调方法中打印一个日志,把当前应用的跟控制器打印出来:
注意:iOS中的打印日志方法和Android也很类似,使用字符串格式化打印即可,一般打印对象的占位符是%@,而如果想打印一个对象特定值的话,就需要实现这个类的description方法,这个和Java中的toString方法一样的。这里在多说几句,就是一般打印基本类型的格式如下:
%d,%i 整型 (%i的老写法)
%hd 短整型
%ld,%lld 长整型
%u 无符整型
%f 浮点型和double型
%0.2f 精度浮点数,只保留两位小数
%x 为32位的无符号整型数(unsigned int),打印使用数字0-9的十六进制,小写a-f;
%X 为32位的无符号整型数(unsigned int),打印使用数字0-9的十六进制,大写A-F;
%o 八进制
%p 指针地址
%s char*类型的字符串
%c char字符类型
%C unichar类型
而这些类型打印格式不用背的,用多了就自然记住了,而且这里用的最多的应该就是%i,%s,%p,%c,%f这几个。
打印结果如下:
看到了当一个应用启动了之后,有哪些生命周期的回调方法会执行,同时看到了根控制器就是我们上面添加HelloWorld控件的入口ViewController类对象。那么这里就引出了两个对象:一个是self.window对象,一个是window.rootViewController对象。下面先来看看self.window这个对象了,其实这个对象是一个UIWindow类型。和Android一样,一个程序就对应一个窗口是后续展示View的基础。
通常一个应用就对应一个UIWindow,就是应用的窗口,但是这话也不能说的太绝对,因为后面不管是Android还是iOS都会出现多屏开发模式,到时候就不止一个Window窗口了。他是后续展示View的基石,如果没有了这个UIWindow的话,那么应用所有的UI都展示不出来,其实UIWindow是一个特殊的View,他继承了UIView类的,也是可以直接添加View控件的以及你可以像使用View那样去操作它:
而上面是说到了,一个应用只能有一个UIWindow,但是我们可以定义多个UIWindow,然后设置指定的UIWindow为主窗口,也是可以的。如果想让哪个窗口成为主窗口并且展示的话,只需要调用makeKeyAndVisible方法即可。
第三、应用的根控制器
上面介绍完了UIWindow知识点,下面在来看一下UIViewController类,在iOS中UIViewController类就相当于Android中的Activity,用于控制View的展示功能,他是一个应用中每个页面的载体,在Android中一个程序都有一个默认或者是主Activity,在iOS中也是一样,有一个根控制器,而这个根控制器就是需要设置到应用的唯一窗口中,也就是UIWindow的rootViewController属性值。关于控制器也有一些生命周期方法,用的最多也是最主要的一个就是View加载完成之后的方法:
– (void)viewDidLoad
这个方法有点类似于Activity的onCreate方法,我们如果想自己添加View的话,就是在这个方法中进行操作的。从上面的那个例子中我们可以看到在这个方法中我们定义了一个UILabel属性,而每个控制器又有一个非常重要的属性就是父View,我们自己需要添加的View都可以通过addSubView方法添加即可。
注意:
在iOS中添加View非常简单和方便,因为任何一个控件包括上面讲到的UIWindow都是继承UIView类,而这些控件可以随便添加其他控件,没有任何限制,比如你可以在一个按钮UIButton中添加一个标签UILabel完全可以的,但是你要是在Android中就不可以了,Android中如果一个控件要添加子View,他必须继承ViewGroup,比如我们常用的几种布局类,这个看来iOS真的比Android简单多了。
我们可以看到一个应用程序肯定会包含多个控制器的,因为一般应用都会包含多个页面,而且每个页面之间还可以跳转,在Android中Activity之间的跳转使用Intent来进行操作的,但是在iOS中没有这种机制,而是用特定的控制器管理类,一般是用到的导航控制器和选项卡控制器来做管理,具体知识到后面接受控制器在详细讲解。
第四、总结四大对象关系
到这里,我们就分析完了一个iOS程序中非常重要的四个对象:UIApplicationDelegate,UIWindow,UIViewController,UIView
从这张图中,可以了解到,一个应用程序一定包含了程序应用的代理对象UIAppDelegate类,应用窗口UIWindow类。然后窗口还有一个根控制器页面UIViewController类,每个控制器都有一个展示UI的父View,根控制器也不例外。最后是每个控制器都会关联一个父View用于展示每个页面的UI。
四、新建一个空项目
当我们知道了一个应用的结构以及关键对象之后,下面我们就来手把手的新建一个空项目,然后自己去新建这四个关键对象。因为Xcode8之后就不在支持新建一个空项目了,为了新建一个空项目,我们可以新建一个项目然后把类都删了,就变成这样了:
第一步:新建一个UIViewController根控制器
一定要选择CocoaTouch Class文件:
到这里我们其实就相当于完成了一个简单的应用程序了,下面就需要在应用的回调方法中设置窗口信息和根控制器信息即可:
这时候运行程序可以看到,一片红色的应用:
从这里就可以看到,一个应用其实最基本的元素一定有UIApplicationDelegate和UIWindow和根控制器即可,那么这个其实也是一个简单的应用了,但是在实际开发中肯定是包含多个控制器的,并且每个根控制器都对应一个View布局文件,所以下面就开始手把手的创建对应的View信息。当然这里可以可以在控制器的回调方法中手动的编码添加View,但是这里我们在介绍iOS中其实和Android一样,每个控制器都有对应的布局文件,这里叫做xib文件。
第二步:新建一个xib文件
选择下一步,命名root.xib即可
新建完成之后,可以看到什么都没有:
我们就需要一步一步的创建了。
第三步:关联xib和控制器
然后我们把当前的xib的File’s Owner设置成我们的根控制器,这里必须得设置xib的File’ Owner,不然会报错的。
所以从这里可以看到,这里的xib其实就相当于Android中的布局文件xml,每个布局文件都是和一个Activity进行关联的,而这里也是一样的原理,一个xib必须依托一个控制器,所以这里的File’s Owner设置对应的控制器即可。
注意:
但是iOS中的xib文件和Android中的布局文件又有点差别,这里xib不仅可以设置视图布局格式,后面再会介绍一个案例,这里可以包含一些特殊的对象,后面介绍如何把控制器,窗口等对象都搞到一个xib中,完全不用编写代码可以实现一个应用的创建了,而关于xib和Xcode默认创建的stroyboard有什么区别呢?就可以简单的理解,stroyboard更好的管理多个控制器和视图之间的关系,比如现在我想创建一个控制器,那么就要创建对应的xib文件了,而一个项目中如果有多个控制器页面,那么工程中就会有多个xib文件,这样可能会不好管理,而stroyborad就可以把这些文件都搞到一起管理,但是从其他iOS老鸟中得知,这个其实还是要具体项目和每个人的开发习惯了,并不是强烈要求一定要用stroyborad的。
第四步:在xib中添加父View
上面设置好了xib文件和控制器的关系,下面还得设置控制器的父View属性值,首先我们得给这个xib中添加一个View,如果不设置的话,运行程序会报错的,添加View很简单,我们在Xcode的最右下角有一个可以选择各种控件和对象的界面:
搜索View控件,找到之后,直接拖到xib内容中即可。托成功之后,我们还需要把控制器的view属性关联到这个View控件中,不然也是会报错的。这里就很简单了,对于Xcode开发工具来说,只要一拖连接即可。如下图所示:
注意:
在iOS中有一个很方便的功能,就是给对象赋值以及后面给控件添加事件,有时候可以选择拖这种快捷方式的,不过得有一个前提,就是这个属性必须设置IBOutlet特性,设置之后就可以看到属性前面有一个小圆圈了:
表示会出现在拖的列表中了,然后如果想赋值给这个属性,就把他拖到指定对象即可,所以中这里也可以看到iOS中的xib和Android中的布局文件有点区别。还有一个是IBAction特性,这个主要是添加控件的事件的,这个一般是添加在一个方法中的,然后这个方法就会出现在拖的列表中,比如我们在控制器中定义一个这个方法:
这里可以看到也是有一个小圆圈的,下面在回去看xib中的File’s Owner:
这里可以看到了,当你去拖doClick方法的右边加号圆圈到一个UIButton控件上的时候,会出现这个界面,其中列举了多个事件,我们可以选中一个之后,就表示这里的UIButton控件的这个事件触发的方法是doClick了。从这个可以看到给View控件赋值和添加事件的方法还是非常方便的,而在Android中就需要findViewById方法,然后在set一些事件回调了,当然Android中现在有一些开源框架做到了这种类似功能,主要采用注解功能,无序findViewById赋值和添加事件了。不过我不怎么喜欢使用这个框架,感觉原生最好!
第五步:加载xib文件
好了,到这里我们就新建好了一个根控制器以及对应的xib文件了,那么下面如果想让这个控制器展示这个xib内容,还需要做一些工作就是要手动的加载这个xib文件,上面那个连接操作只是简单的建立关系,加载还得自己写代码,就相当于Android中的setContentView方法效果一下,这里就需要改一下控制器的初始化方法了:
控制器提供了一个initWithNibName:bundle:方法,这个方法可以显示的加载一个xib文件,这里需要注意两点就是xib文件名不能携带后缀名,其次就是第二个参数表示xib文件的路径,传入nil表示从项目的根目录中查找。
设置了控制器加载xib代码之后,下面就可以运行程序了:
五、知识点补充
xib文件的解析过程:
1> 创建好Objects下面的所有对象
2> 建立好Objects下面的所有对象和File’s Owner的关联关系
在上面介绍xib中说到了,xib不仅可以添加控件视图,也可以添加各种对象的,比如这里我们可以添加一个UIWindow和UIViewController对象:
看到了吧,一个xib其实可以管理一个简单的应用程序的所有对象的,然后我们可以这是这些对象的关系就好了。
self.viewController = [[RootViewController alloc] init];(创建好控制器之后,并没有马上初始化它内部的view)
2> 设置控制器为UIWindow的根控制器
self.window.rootViewController = self.viewController;(设置完根控制器之后并没有将控制器的view添加到窗口中)
3> 显示窗口,并且把控制器的view添加到窗口中
[self.window makeKeyAndVisible];
首先调用控制器的loadView进行初始化控制器的view(可以通过xib文件\可以通过代码创建)
第三、UIViewController生命周期方法的调用顺序
1> view初始化完毕后,就会调用控制器的viewDidLoad方法
2> view初始化完毕后,就会把这个根控制器的view添加到窗口中
3> 当view即将被添加到窗口中时,就会调用控制器的viewWillAppear:方法
4> 当view已经被添加到窗口中时,就会调用控制器的viewDidAppear:方法
5> 如果控制器的view即将从窗口中移除时,就会调用控制器的viewWillDisappear:方法
6> 如果控制器的view已经从窗口中移除时,就会调用控制器的viewDidDisappear:方法
7> 如果控制器接收到内存警告的时候,就会调用控制器的didReceiveMemoryWarning方法
didReceiveMemoryWarning方法的默认实现是:如果控制器的view没有显示在窗口中,也就是说controller.view.superview为nil时,系统就会销毁控制器的view.
9> 如果控制器的view以前因为内存警告被销毁过,现在需要再次访问控制器的view时,会重复前面的步骤初始化view
六、总结
转载请注明:尼古拉斯.赵四 » iOS狂暴之路—iOS的第一个应用中能学到哪些知识