View和ViewController的生命周期问题(好文章)

一、 ViewController 的职责
对内管理与之关联的 View ,对外跟其他 ViewController 通信和协调。对于与之关联的 View ViewController 总是在需要的时候才加载视图,并在不需要的时候卸载视图,所以也同时担当了管理应用资源的责任
二、 ViewController 的生命周期
View 是指 Controller View 。它作为 Controler 的属性,生命周期在 Controller 的生命周期内。就是说你的 Controller 不能在 view 释放前就释放了。
                                          viewController 的生命周期图
init -> loadView ->viewDidLoad-> viewWillAppear->viewDidAppear->viewWillDisappear->viewDidDisappear->viewDidUnload->dealloc

      需要说明的是:当你 alloc init 了一个 ViewController 时,这个 ViewController 应该是还没有创建 view 的。 ViewController view 是使用了 lazyInit 方式创建,就是说你调用的 view 属性的 getter :[ self view ]。在 getter 里会先判断 view 是否创建,如果没有创建,那么会调用 loadView 来创建 view loadView 完成时会继续调用 viewDidLoad loadView viewDidLoad 的一个区别就是: loadView 时还没有 view 。而 viewDidLoad view 以及创建好了。
 
三、 view 的加载过程
     
跟随如下文字理解 viewController view 加载过程:
1  先判断子类是否重写了 loadView ,如果有直接调用。之后调 viewDidLoad 完成 View 的加载。
如果是外部通过调用 initWithNibName:bundle 指定 nib 文件名的话, ViewController 记载此 nib 来创建 View
如果 initWithNibName:bundle name 参数为 nil ,则 ViewController 会通过以下两个步骤找到与其关联的 nib
如果类名包含 Controller ,例如 ViewController 的类名是 MyViewController ,则查找是否存在 MyView.nib
找跟 ViewController 类名一样的文件,例如 MyViewController ,则查找是否存在 MyViewController.nib
4   如果子类没有重写的 loadView ,则 ViewController 会从 StroyBoards 中找或者调用其默认的 loadView ,默认的 loadView 返回一个空白的 UIView 对象。
注意第一步, ViewController 是判断子类是否重写了 loadView ,而不是判断调用子类的 loadView 之后 ViewController View 是否为空。就是说,如果子类重写了 loadView 的话,不管子类在 loadView 里面能否获取到 View ViewController 都会直接调 viewDidLoad 完成 View 的加载。
 
跟随以下文字理解卸载过程:
系统发出警告或者 ViewController 本身调用导致 didReceiveMemoryWarning 被调用
调用 viewWillUnload 之后释放 View
调用 viewDidUnload
 
四、模拟器的调用顺序
我构架了这样一个环境,在该环境中有两个 viewController ,姑且命名为 A B tag 分别为 1 2 A 控制程序启动的时候即加载的界面,在 A 中放一个按钮,按下后会通过 segue 来调用到界面 B B  中页放一个按钮,通过执行
[self dismissModalViewControll erAnimated:YES];
来返回界面 A
然后检测所有的函数调用,依次如下
加载 A 的时候依次调用
1 initWithCoder
1 loadView // 如果说你进行了重写,会在这里调用,这一步可以参考下文
1 viewDidLoad
1 viewWillAppear
1 viewWillLayoutSubviews
1 viewDidLayoutSubviews
1 viewDidAppear
切换至 B 的时候依次调用
2 initWithCoder                         // 先将 2 初始化
1 prepareForSegue       // 调用 1 的准备过度的函数,所以在该函数中可以对界面 B 的一些相关属性进行赋值
2 loadView       // 如果这里进行了重写
2 viewDidLoad                           //2 界面加载
1 viewWillDisappear
2 viewWillAppear
2 viewWillLayoutSubviews
2 viewDidLayoutSubviews
2 viewDidAppear
1 viewDidDisappear
B 切换回 A 的时候依次调用
2 viewWillDisappear
1 viewWillAppear
1 viewDidAppear
2 viewDidDisappear
2 dealloc
顺序总结下来加载依次为:加载  -  显示  -  布局
完成顺序依次为:完成布局  -  完成显示   -  完成加载
 
小注: -(void)loadView; 函数如果重写,下面是一个可能的 demo
-(void)loadView
{
      CGRect applicationFrame = [[UIScreenmainScreen] applicationFrame];
      UIView *contentView = [[UIViewalloc] initWithFrame:applicationFrame];
      contentView.backgroundColor = [UIColordarkGrayColor];
      self.view = contentView;
     
      UILabel *lab = [[UILabelalloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
      lab.text = @"HelloWorld";
      [self.viewaddSubview:lab];
}
loadView 虽然返回值为空,但必须在函数体内对 self.view 进行赋值,否则会在建立该界面的时候收到如下的 log 信息:
Application windows are expected to have a root view controller at the end of application launch
具体执行顺序为:代码执行了 initWithCoder 之后直接调用了三次 loadView 函数,并且没有调用其它函数(包括 viewDidLoad  viewWillDisappear viewWillLayoutSubviews
疑问:
暂不清楚为什么会调用三次,我的猜测是:上述三个函数分别检测了一遍 view 是否存在,发现不存在,所以各自调用了一遍 viewLoad ,最后发现依然不存在,所以上述三个函数分别返回了失败,加载完成
但矛盾的地方是:为什么上述三个函数本身没有执行到?底层到底做了什么?
 
五、 view ViewController 的创建阶段,关于什么时候应该干什么
1 init
Allocating critical data structures required by your view controller
不要出现创建 view 的代码。良好的设计,在 init 里应该只有相关数据的初始化,而且这些数据都是比较关键的数据。 init 里不要掉 self.view ,否则会导致 viewcontroller 创建 view 。(因为 view lazyinit 的)。
2 loadView
Creating your view objects
只初始化 view ,一般用于创建比较关键的 view tableViewController tabView UINavigationController navgationBar ,不可掉用 view getter (在掉 super loadView 前),最好也不要初始化一些非关键的 view 。如果你是从 nib 文件中创建的 viewController 在这里一定要首先调用 super loadView 方法,但建议不要重载这个方法。
3 viewDidLoad
Allocating or loading data to be displayed in your view
这时候 view 已经有了,最适合创建一些附加的 view 和控件了。有一点需要注意的是, viewDidLoad 会调用多次( viewcontroller 可能多次载入 view ,参见图 2 )。
4 viewWillAppear  这个一般在 view 被添加到 superview 之前,切换动画之前调用。在这里可以进行一些显示前的处理。比如键盘弹出,一些特殊的过程动画(比如状态条和 navigationbar 颜色)。
5 viewDidAppear  一般用于显示后,在切换动画后,如果有需要的操作,可以在这里加入相关代码。
6 viewDidUnload
Releasing references to view objects
Releasing data that is not needed when your view is not displayed
这时候 viewController view 已经是 nil 了。由于这一般发生在内存警告时,所以在这里你应该将那些不在显示的 view 释放了。比如你在 viewcontroller view 上加了一个 label ,而且这个 label viewcontroller 的属性,那么你要把这个属性设置成 nil ,以免占用不必要的内存,而这个 label viewDidLoad 时会重新创建。
7 dealloc
Releasing critical data structures required by your view controller
六、几点备注:
1 、按结构可以对 iOS 的所有 ViewController 分成两类:
1 )、主要用于展示内容的 ViewController ,这种 ViewController 主要用于为用户展示内容,并与用户交互,如 UITableViewController UIViewController
2 )、用于控制和显示其他 ViewController ViewController 。这种 ViewController 一般都是一个 ViewController 的容器。如 UINavigationController UITabbarController 。它们都有一个属性: viewControllers 。其中 UINavigationController 表示一种 Stack 式结构, push 一个 ViewController pop 一次,因此后一个 ViewController 一般会依赖前一个 ViewController 。而 UITabbarController 表示一个 Array 结构,各个 ViewController 是并列的。
第一种 ViewController 会经常被继承,用来显示不同的数据给用户。而第二种很少被继承,除非你真的需要自定义它。
 
2 、当 view 被添加其他 view 中之前时,会调用 viewWillAppear ,而之后会调用 viewDidAppear
view 从其他 view 中移出之前时,会调用 viewWillDisAppear ,而之后会调用 viewDidDisappear
view 不在使用,而且是 disappeared ,受到内存警告时,那么 viewController 会将 view 释放并将其指向 nil
 
3 、由于 Controller 加载 View 时,会自动将一些 View 对象指向其对应的 IBOutlet 变量。
所以当 view 被卸载时我们必须在 viewDidUnload 将这些变量 release 掉, ViewController 不会自动做这件事。

具体做法是将变量设置为空,(注意和dealloc中将变量release的区别)注意此时Controllerview属性是空的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值