ViewController的生命周期分析和使用
一、结构
按结构可以对 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 会经常被继承,用来显示不同的数据给用户。而第二种很少被继承,除非你真的需要自定义它。
注:细心的同学应该能发现,在 Xcode 中新建一个 ViewController 时,只可以选择继承自 UIViewController 和 UITableViewController ,而它们都是第一种。
图 1
二、 Controller 和 View 的生命周期
这里指的 View 是指 Controller 的 View 。它作为 Controler 的属性,生命周期在 Controller 的生命周期内。就是说你的 Controller 不能在 view 释放前就释放了。
图 2 ViewController 生命周期
当你 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 被添加其他 view 中之前时,会调用 viewWillAppear ,而之后会调用 viewDidAppear 。
当 view 从其他 view 中移出之前时,会调用 viewWillDisAppear ,而之后会调用 viewDidDisappear 。
当 view 不在使用,而且是 disappeared ,受到内存警告时,那么 viewController 会将 view 释放并将其指向 nil 。
三、代码组织(如何设计良好的 viewcontroller )
ViewController 生命周期中有那么多函数,一个重要问题就是什么代码该写在什么地方。
1 、 init里不要出现创建view 的代码。良好的设计,在 init 里应该只有相关数据的初始化,而且这些数据都是比较关键的数据。 init 里不要掉 self .view ,否则会导致 viewcontroller 创建 view 。(因为 view 是 lazyinit 的)。
2 、 loadView 中只初始化 view ,一般用于创建比较关键的 view 如 tableViewController 的 tabView , UINavigationController 的 navgationBar ,不可掉用 view 的 getter (在掉 super loadView 前),最好也不要初始化一些非关键的 view 。如果你是从 nib 文件中创建的 viewController 在这里一定要首先调用 super 的 loadView 方法,但建议不要重载这个方法。
3 、 viewDidLoad 这时候 view 已经有了,最适合创建一些附加的 view 和控件了。有一点需要注意的是, viewDidLoad 会调用多次( viewcontroller 可能多次载入 view ,参见图 2 )。
4 、 viewWillAppear 这个一般在 view 被添加到 superview 之前,切换动画之前调用。在这里可以进行一些显示前的处理。比如键盘弹出,一些特殊的过程动画(比如状态条和 navigationbar 颜色)。
5 、 viewDidAppear 一般用于显示后,在切换动画后,如果有需要的操作,可以在这里加入相关代码。
6 、 viewDidUnload 这时候 viewController 的 view 已经是 nil 了。由于这一般发生在内存警告时,所以在这里你应该将那些不在显示的 view 释放了。比如你在 viewcontroller 的 view 上加了一个 label ,而且这个 label 是 viewcontroller 的属性,那么你要把这个属性设置成 nil ,以免占用不必要的内存,而这个 label 在 viewDidLoad 时会重新创建。
7
、
接下来我们看看
ViewController
中的
view
是如何被卸载的:
从图中可以看到,当系统发出内存警告时,会调用 didReceiveMemoeryWarning 方法,如果当前有能被释放的 view ,系统会调用 viewWillUnload 方法来释放 view ,完成后调用 viewDidUnload 方法,至此, view 就被卸载了。此时原本指向 view 的变量要被置为 nil ,具体操作是在 viewDidUnload 方法中调用 self .myButton = nil ;
从图中可以看到,当系统发出内存警告时,会调用 didReceiveMemoeryWarning 方法,如果当前有能被释放的 view ,系统会调用 viewWillUnload 方法来释放 view ,完成后调用 viewDidUnload 方法,至此, view 就被卸载了。此时原本指向 view 的变量要被置为 nil ,具体操作是在 viewDidUnload 方法中调用 self .myButton = nil ;
小结一下:
loadView
和
viewDidLoad
的区别就是,
loadView
时
view
还没有生成,
viewDidLoad
时,
view
已经生成了,
loadView
只会被调用一次,而
viewDidLoad
可能会被调用多次(
View
可能会被多次加载),当
view
被添加到其他
view
中之前,会调用
viewWillAppear
,之后会调用
viewDidAppear
。当
view
从其他
view
中移除之前,调用
viewWillDisAppear
,移除之后会调用
viewDidDisappear
。当
view
不再使用时,受到内存警告时,
ViewController
会将
view
释放并将其指向为
nil
。
ViewController 的生命周期中各方法执行流程如下: init—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—>viewWillDisapper—>viewDidDisapper—>viewWillUnload->viewDidUnload—>dealloc
ViewController 的生命周期中各方法执行流程如下: init—>loadView—>viewDidLoad—>viewWillApper—>viewDidApper—>viewWillDisapper—>viewDidDisapper—>viewWillUnload->viewDidUnload—>dealloc