在上一篇文章iOS开发:第一个iOS程序分析——代理,生命周期函数中主要介绍了iOS使用Objective-C开发的两个主要文件main.m和AppDelegate.m和控制程序生命周期的函数,接下来将介绍另外两个文件:AppDelegate.h文件、UIViewController.m文件和iOS开发中的View和ViewController。
打开AppDelegate.h文件:
先看一下AppDelegate类,前面说过,AppDelegate是作为Application的代理,这个程序中的AppDelegate不仅负责控制程序的生命周期,而且负责控制程序初始界面(初始StoryBoard)的显示,因此该代理类必须能够访问到初始界面的界面元素。
明白这点,就不难了解KZX AppDelegate类中为什么会有UIWindow的一个引用了。如下
该文件只有以下寥寥几行代码:
<span style="font-family:Comic Sans MS;font-size:14px;">#import <UIKit/UIKit.h>
@interface KZXAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
</span>
除去导入头文件的语句,文件由@interface开始声明到@end结束声明,可以看到,确实该代理类持有一个UIWindow类的实例,该UIWindow实例即代表了该视图控制器所负责控制显示的窗口视图(即一个屏幕界面),读者朋友可以尝试把这一句删去,会发现运行程序后是黑漆漆一片,因为代理无法找到应该在哪里显示视图。自然没有界面。
那么为什么代理要持有UIWindow的实例而不持有其他视图的实例呢?
先来看看官方文档对于视图的说明(View):
“Views not only display themselves onscreen and react to user input, they also serve as containers for other views. As a result, views in an app are arranged in a hierarchical structure called the view hierarchy. The view hierarchy defines the layout of views relative to other views. Within that hierarchy, view instances enclosed within a view are called subviews, and the parent view that encloses a view is referred to as its superview. Even though a view instance can have multiple subviews, it can have only one superview.At the top of the view hierarchy is the window object.”
"视图不仅在屏幕上显示他们本身、对用户的输入做出反应,而且也能够作为容器来为其他视图提供服务。所以一个app中的视图是被组织成一种层次结构的,叫做视图层级。视图层级决定了不同但是相关的视图之间的布局和排版。在视图层级里面,多个视图围绕某个特定的视图来布局,则该特定的视图称为父视图,而围绕父视图布局的其他视图称为子视图。尽管一个视图能够拥有多个子视图但是一个视图不可能有多个父视图。在视图层级的最高层次是window对象"。
注意最重要的是最后一句话,也就是说如果把视图层级看成一棵树的话,那么这棵树的根就是window对象,因此学过树数据结构的都知道,持有根节点当然能方便地访问其他所有节点,这就是AppDelegate持有UIWindow的原因。
注意到@interface AppDelegate后还带了: UIResponder <UIApplicationDelegate>,在Objective-C中,继承写法跟C++一样都是用冒号(半角)来表示,所以这一句表示了该代理类继承了UIResponder类,但是后半部分<UIApplicationDelegate>则体现了Objective-C还有的 协议 这种概念,协议类似于Java中的接口,与接口不同的是实现协议可以不实现协议的所有方法,具体要看协议声明方法时是用@optional还是@required。可在Xcode中command+点击UIApplicationDelegate看看,如下。
<span style="font-family:Comic Sans MS;font-size:14px;">@protocol UIApplicationDelegate<NSObject>
@optional
- (void)applicationDidFinishLaunching:(UIApplication *)application;
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions NS_AVAILABLE_IOS(6_0);
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions NS_AVAILABLE_IOS(3_0);
- (void)applicationDidBecomeActive:(UIApplication *)application;
- (void)applicationWillResignActive:(UIApplication *)application;
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url; // Will be deprecated at some point, please replace with application:openURL:sourceApplication:annotation:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation NS_AVAILABLE_IOS(4_2); // no equiv. notification. return NO if the application can't open for some reason
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application; // try to clean up as much memory as possible. next step is to terminate app
- (void)applicationWillTerminate:(UIApplication *)application;
- (void)applicationSignificantTimeChange:(UIApplication *)application; // midnight, carrier time update, daylight savings time change
- (void)application:(UIApplication *)application willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration;
- (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation;
- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame; // in screen coordinates
- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame;
</span>
接下来就是KZXViewController.m(KZXViewController.h几乎没有内容):
先来看看Apple对视图控制器的一些官方文档(ViewController):
“In an iOS app, you use a view controller (UIViewController) to
manage a content view with its hierarchy of subviews.A view
controller isn’t part of the view hierarchy and it’s not an element
in your interface. Instead, it manages the view objects in the hierarchy
and provides them with behavior.
You also use view controllers to implement transitions from one type of
content to another. Because iOS apps have a limited amount of space in
which to display content, view controllers provide the infrastructure
needed to remove the views of one view controller and replace them
with the views of another.”
“在一个iOS 的 app 中,你使用一个视图控制器(UIViewController)来
管理一个具有内容的视图以及他的子视图层级。注意视图控制器并不是视
图层级的一部分,而且也不属于用户界面的元素。实际上,他是用来管理
视图层级中的视图对象的,并且为他们定义一些响应行为(来作为与用户
的交互)。当然也可以使用视图控制器来作为两个(种)具有内容的视图
的过渡,毕竟iOS app(实际上应该是iOS的大部分设备屏幕)只有有限的
空间来显示内容,视图控制器提供了删除视图和替换视图的一些必须的基础函数。“
由以上可知,一个视图的视图控制器实际负责两部分——管理该视图的
子视图层级,管理该视图与其他视图的转换。
回到KZXViewController.m文件,该文件中定义了如下函数:
<span style="font-family:Comic Sans MS;font-size:14px;">- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
- (void)viewDidLoad
- (void)didReceiveMemoryWarning
</span>
initWithNibName使用一个xib文件名(视图布局文件)来初始化视图
的子视图层级,viewDidLoad在初始化完毕后调用(两个函数对应“管理
子视图层级”功能),而didReceiveMemoryWarning通常在内存不足时
被警告时调用,此函数一般用来释放一些资源和数据。
关于体现视图控制器“管理该视图与其他视图转换”的功能,
读者朋友可以打开UIViewController头文件看声明的方法对应。
总结:
1.代理通过适时调用委托方的函数来控制对委托方的访问,ApplicationDelegate
作为整个app的代理不仅要控制app的生命周期,还要负责app入口界面,因此
ApplicationDelegate持有app入口界面的视图层级根视图——UIWindow。
2.视图控制器负责管理该视图的子视图层级以及管理该视图与其他视图的转换。
视图控制器不属于视图,不在视图层级之内。
下一次我们将介绍MainStoryBoard.storyboard文件以及常用的一些视图控件的使用。