iOS ViewController生命周期

来源:互联网 发布:制作相册软件下载 编辑:程序博客网 时间:2024/05/19 16:03

ViewController

ViewController是IOS开发中MVC模式中的C,ViewController是view的controller,ViewController的职责主要包括管理内部各个view的加载显示和卸载,同时负责与其他ViewController的通信和协调。

在IOS中,有两类ViewController,一类是显示内容的,比如UIViewController、UITableViewController等,同时还可以自定义继承自UIViewController的ViewController;另一类是ViewController容器,UINavigationViewController和UITabBarController等,UINavigationController是以Stack的形式来存储和管理ViewController,UITabBarController是以Array的形式来管理ViewController。


View的加载

从图中可以看到,在view加载过程中首先会调用loadView方法,在这个方法中主要完成一些关键view的初始化工作,比如UINavigationViewController和UITabBarController等容器类的ViewController;接下来就是加载view,加载成功后,会接着调用viewDidLoad方法,这里要记住的一点是,在loadView之前,是没有view的,也就是说,在这之前,view还没有被初始化。完成viewDidLoad方法后,ViewController里面就成功的加载view了,如上图右下角所示。

在Controller中创建view有两种方式,一种是通过代码创建、一种是通过Storyboard或Interface Builder来创建,后者可以比较直观的配置view的外观和属性,Storyboard配合IOS6后推出的AutoLayout,应该是Apple之后主推的一种UI定制解决方案,后期我会专门介绍一篇使用AutoLayout进行UI制作的文章。言归正传,通过IB或Storyboard创建view,在Controller中创建view后,会在Controller中对view进行一些操作,会出现如下代码

[cpp] view plaincopy
  1. @interface MyViewController()  
  2. @property (nonatomic) IBOutlet id myButton;  
  3. @property (nonatomic) IBOutlet id myTextField;  
  4.    
  5. (IBAction)myAction:(id)sender;  
  6. @end  

这里用IBOutlet标记了一个UIButton和一个UITextField,用IBAction来标记UIButton的响应事件,IBOutlet和IBAction都是一个整形常量,用来标记控件,通过一张图能比较清晰的看清他们之间的关系:



上图中,MyViewController是继承自UIViewController的一个自定义ViewController,它包含两个View,一个是UIButton,一个是UITextField,从箭头的指向性上就可以比较好的理解IBOutlet和IBAction了。IBOutlet是告诉Interface Builder,此实例变量被连接到nib文件中的view对象,IBOutlet本身不做任何操作,只是一个标记作用。IBAction同样是个标记关键字,它只能标记方法,它告诉IB用IBAction标记的方法可以被某个控件触发。

通过编程的方式创建view,如下代码:

[cpp] view plaincopy
  1. (void)loadView  
  2.  
  3.     CGRect applicationFrame [[UIScreen mainScreen] applicationFrame];  
  4.     UIView *contentView [[UIView alloc] initWithFrame:applicationFrame];  
  5.     contentView.backgroundColor [UIColor blackColor];  
  6.     self.view contentView;  
  7.    
  8.     levelView [[LevelView alloc] initWithFrame:applicationFrame viewController:self];  
  9.     [self.view addSubview:levelView];  
  10.  
上述代码首先得到屏幕的frame,然后根据该frame生成了一个contentView,并指定当前ViewController的root view为contentView,然后生成了一个LevelView的自定义View并将它通过addSubview:方法添加到当前ViewController当中,完成view的初始化加载。

 

关于loadView方法的重写,官方文档中有一个明显的注释,原文如下:

Note: When overriding the loadView method to create your views programmatically, you should not call super. Doing so initiates the default view-loading behavior and usually just wastes CPU cycles. Your own implementation of the loadView method should do all the work that is needed to create a root view and subviews for your view controller.

意思是当通过代码方式去创建你自己的view时,在loadView方法中不应该调用super,如果调用[super loadView]会影响CPU性能。

代码创建界面文件

1.创建新的Empty Application Project

2.新建ViewController的类,添加loadView方法,及viewDidLoad等方法

复制代码
#import "XYZViewController.h"@interface XYZViewController ()@end@implementation XYZViewController- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];    if (self) {        // Custom initialization    }    return self;}- (void)loadView{    UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];        contentView.backgroundColor = [UIColor blueColor];        self.view = contentView;}- (void)viewDidLoad{    [super viewDidLoad];    // Do any additional setup after loading the view.    NSLog(@"View Did Load");}- (void)viewWillAppear:(BOOL)animated{    NSLog(@"View Will Appear");}- (void)viewDidAppear:(BOOL)animated{    NSLog(@"View Did Appear");}- (void)viewWillDisappear:(BOOL)animated{    NSLog(@"View Will Disappear");}- (void)viewDidDisappear:(BOOL)animated{    NSLog(@"View Did Disappear");}@end
复制代码

3.在AppDelegate.m中的application:didFinishLaunchingWithOptions:中注册ViewController

复制代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];    // Override point for customization after application launch.    self.window.backgroundColor = [UIColor whiteColor];    XYZViewController *viewController = [[XYZViewController alloc]initWithNibName:nil bundle:nil];    self.window.rootViewController = viewController;    [self.window makeKeyAndVisible];    return YES;}
复制代码

虽然我们可以在AppDelegate.m中的application:didFinishLaunchingWithOptions:中设置window的代码后面添加view,但是在一般的工程中,我们不会在委托类中管理我们的View。 而是利用委托类中的UIWindow去添加UIViewController,再在ViewController类中去管理View。

附,创建带NavigationController的代码

复制代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];    self.window.backgroundColor = [UIColor whiteColor];    /*设置Navigation controller*/    XYZFirstViewController *viewController = [[XYZFirstViewController alloc] initWithNibName:nil bundle:nil];    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController];    [nav setNavigationBarHidden:YES animated:NO];    self.window.rootViewController = nav;    [self.window makeKeyAndVisible];    [viewController release];    [nav release];    return YES;}
复制代码

 

ViewController生命周期

alloc -> initWithNibName -> loadView -> viewDidLoad -> viewWillAppear -> viewDidAppear -> viewWillDisappear -> viewDidDisappear -> dealloc

注意viewWillUnload和viewDidUnload已经在ios6被废弃了,因为Clearing references to views is no longer necessary。

注意1.没有viewWillLoad。

注意2.viewDidLoad和viewDidUnload并不是成对的。

 

启动程序

2014-07-28 17:43:36.124 ViewLifeCycle[4007:a0b] View Did Load
2014-07-28 17:43:36.125 ViewLifeCycle[4007:a0b] View Will Appear
2014-07-28 17:43:36.128 ViewLifeCycle[4007:a0b] View Did Appear

按下Home键,并没有任何记录

双击Home键,删除该程序
2014-07-28 17:43:51.327 ViewLifeCycle[4007:a0b] View Will Disappear
2014-07-28 17:43:51.327 ViewLifeCycle[4007:a0b] View Did Disappear

为什么按下Home键之后没有调用viewWillDisappear和viewDidDisappear呢?

因为在ios4后引入了后台的概念,当按下Home键之后,程序被挂起了,但是该View依然是原来的View,并不是新的。所以只有内存不够的时候或程序被终止的时候,才会调用viewWillDisappear和viewDidDisappear。

 

View的卸载

从图中可以看到,当系统发出内存警告时,会调用didReceiveMemoeryWarning方法,如果当前有能被释放的view,系统会调用viewWillUnload方法来释放view,完成后调用viewDidUnload方法,至此,view就被卸载了。此时原本指向view的变量要被置为nil,具体操作是在viewDidUnload方法中调用self.myButton = nil;


loadView v.s. viewDidLoad

view的nib文件为nil时,手工创建视图界面时调用loadView;当view的nib文件存在的时候,初始化工作在viewDidLoad中实现。

loadView时view还没有生成,viewDidLoad时,view已经生成了,loadView只会被调用一次,而viewDidLoad可能会被调用多次(View可能会被多次加载),当view被添加到其他view中之前,会调用viewWillAppear,之后会调用viewDidAppear。当view从其他view中移除之前,调用viewWillDisAppear,移除之后会调用viewDidDisappear。当view不再使用时,受到内存警告时,ViewController会将view释放并将其指向为nil。

ViewController的生命周期中各方法执行流程如下:

alloc—>init—>awakeFromNib—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—>viewWillDisapper—>viewDidDisapper—>dealloc

1. + (id)alloc 分配内存;

2. - (id)init 方法(包括其他-(id)init...方法),只允许调用一次,并且要与 alloc方法 写在一起,在init方法中申请的内存,要在dealloc方法中释放(或者其他地方);

3. - (void)awakeFromNib 使用Xib初始化后会调用此方法,一般不会重写此方法;

4. - (void)loadView 如果使用Xib创建ViewController,就不要重写该方法。一般不会修改此方法;

5. - (void)viewDidLoad 视图加载完成之后被调用,这个方法很重要,可以在此增加一些自己定义的控件,注意此时view的frame不一定是显示时候的frame,真实的frame会在 - (void)viewDidAppear: 后。在iOS6.0+版本中在对象的整个生命周期中只会被调用一次,这里要注意在iOS3.0~iOS5.X版本中可能会被重复调用,当ViewController收到内存警告后,会释放View,并调用viewDidUnload,之后会重新调用viewDidLoad,所以要支持iOS6.0以前版本的童鞋要注意这里的内存管理。
6. - (void)viewWillAppear:(BOOL)animated view 将要显示的时候,可以在此加载一些图片,和一些其他占内存的资源;
7. - (void)viewDidAppear:(BOOL)animated view 已经显示的时候;

8. - (void)viewWillDisappear:(BOOL)animated view 将要隐藏的时候,可以在此将一些占用内存比较大的资源先释放掉,在 viewWillAppear: 中重新加载。如:图片/声音/视频。如果View已经隐藏而又在内存中保留这些在显示前不会被调用的资源,那么App闪退的几率会增加,尤其是ViewController比较多的时候;

9. - (void)viewDidAppear:(BOOL)animated view 已经隐藏的时候;

10. - (void)dealloc,不要手动调用此方法,当引用计数值为0的时候,系统会自动调用此方法。

当受到内存警告时,那么此时系统默认操作会检查当前视图控制器的view是否还在使用,如果没在使用且控制器实现了loadView方法,ViewController会将view release并将其指向为nil。

注意,不要在loadView中调用父类方法[super loadView],因为这会影响CPU性能。

注意2,切换前后台不会调用viewWillAppear

0 0
原创粉丝点击