# 浅谈ViewController生命周期
前言:在编写 ViewController 的时候我们可能会有疑问,ViewController 的生命周期方法有哪些?调用的次序是什么?本文将会以案例的方式介绍。
# 生命周期函数
在讲述 ViewController 生命周期函数调用次序之前,需要了解有哪些生命周期函数:
// 类的初始化方法
+ (void)initialize;
//通过xib来初始化控制器
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil ;
// 对象初始化方法
- (instancetype)init;
// 从归档初始化
- (instancetype)initWithCoder:(NSCoder *)coder;
//归档初始化后唤醒nib
-(void)awakeFromNib;
// 加载视图:当访问UIViewController的view属性时,view如果此时是nil,那么VC会自动调用loadView方法来初始化一个UIView并赋值给view属性。此方法用在初始化关键view,需要注意的是,在view初始化之前,不能先调用view的getter方法,否则将导致死循环(除非先调用了[super loadView];)如果没有重载loadView方法,则UIViewController会从nib或StoryBoard中查找默认的loadView,默认的loadView会返回一个空白的UIView对象。
-(void)loadView;
// 视图加载完成
- (void)viewDidLoad;
// 将要展示:,在view即将添加到视图层级中(显示给用户)且任意显示动画切换之前调用,此时self.view.superview为nil.这个方法中完成任何与试图显示相关的任务,例如改变视图方向、状态栏方向、视图显示样式等。
-(void)viewWillAppear:(BOOL)animated;
// 将要布局子视图,self.view.superview为_UIParallaxDimmingView
-(void)viewWillLayoutSubviews;
// 已经布局子视图
-(void)viewDidLayoutSubviews;
// 已经展示:在view被添加到视图层级中,显示动画切换之后调用(这时view已经添加到supperView中)。在这个方法中执行视图显示相关附件任务,如果重载了这个方法,必须在方法中调用[supper viewDidAppear];,此时self.view.superview为UIViewControllerWrapperView。
-(void)viewDidAppear:(BOOL)animated;
// 将要消失:view即将从supperView中移除,移除动画切换之后调用,此时已调用removeFromSuperview。此时self.view.superview还是superview为UIViewControllerWrapperView.
-(void)viewWillDisappear:(BOOL)animated;
// 已经消失:view从superView中移除,移除动画切换之后调用,此时已调用removeFromSuperview。此时self.view.superview还是superview为nil.
-(void)viewDidDisappear:(BOOL)animated;
// 内存警告
- (void)didReceiveMemoryWarning;
// 销毁释放
-(void)dealloc;
# segue: A push to B , B pop 调用次序
说明:此处的 pop 不仅是 popViewControllerAnimated
也可能是左滑返回
# A 出现的次序
2020-09-01 14:18:36.592169+0800 LifecycleDemo[34988:1763624] loadView A
2020-09-01 14:18:36.592287+0800 LifecycleDemo[34988:1763624] viewDidLoad A
2020-09-01 14:18:36.592552+0800 LifecycleDemo[34988:1763624] viewWillAppear A
2020-09-01 14:18:36.593958+0800 LifecycleDemo[34988:1763624] viewWillLayoutSubviews A
2020-09-01 14:18:36.594046+0800 LifecycleDemo[34988:1763624] viewDidLayoutSubviews A
2020-09-01 14:18:36.624485+0800 LifecycleDemo[34988:1763624] viewDidAppear A
# A push到 B
2020-09-01 14:20:07.798123+0800 LifecycleDemo[34988:1763624] loadView B
2020-09-01 14:20:07.798232+0800 LifecycleDemo[34988:1763624] viewDidLoad B
2020-09-01 14:20:07.798346+0800 LifecycleDemo[34988:1763624] viewWillDisappear A
2020-09-01 14:20:07.798461+0800 LifecycleDemo[34988:1763624] viewWillAppear B
2020-09-01 14:20:07.814858+0800 LifecycleDemo[34988:1763624] viewWillLayoutSubviews B
2020-09-01 14:20:07.814982+0800 LifecycleDemo[34988:1763624] viewDidLayoutSubviews B
2020-09-01 14:20:08.318965+0800 LifecycleDemo[34988:1763624] viewDidDisappear A
2020-09-01 14:20:08.319101+0800 LifecycleDemo[34988:1763624] viewDidAppear B
# B pop
2020-09-01 14:23:04.113623+0800 LifecycleDemo[34988:1763624] viewWillDisappear B
2020-09-01 14:23:04.113744+0800 LifecycleDemo[34988:1763624] viewWillAppear A
2020-09-01 14:23:04.618726+0800 LifecycleDemo[34988:1763624] viewDidDisappear B
2020-09-01 14:23:04.618908+0800 LifecycleDemo[34988:1763624] viewDidAppear A
2020-09-01 14:23:04.678278+0800 LifecycleDemo[35825:1773001] dealloc B
注意点:
- A push to B 下,B 展示的时候在 B
viewWillAppear
即将展示前会viewWillDisappear A
在 B 展示前 A 会viewDidDisappear
- 在 B pop 时会先调用 B 的即将消失
viewWillDisappear
,然后才会调用 A 的即将展示viewWillAppear
总结
针对 pop 的情况,被压入堆栈的页面不会触发销毁
dealloc
,弹出堆栈页面的会触发dealloc
;对于新打开的页面整体触发流程为: loadView -> viewDidLoad -> viewWillAppear -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear ;
经过 push 的方式展示的页面,在调用
viewWillAppear
之前会调用“前一个(A->B A 在前;B pop A A 在前)”页面的viewWillDisAppear
; 在viewDidAppear
调用之前会调用”前一个“页面的viewDidDisAppear
;
# A pushViewController to B
NextControllerViewController *ctr = [[NextControllerViewController alloc] init];
[self.navigationController pushViewController:ctr animated:YES];
总结: 生命周期调用次序同A push to B , B pop 调用次序
# A replace(show detail) to B , 下滑展示 B
# A展示
2020-09-01 14:35:32.949604+0800 LifecycleDemo[36547:1782572] loadView A
2020-09-01 14:35:32.949774+0800 LifecycleDemo[36547:1782572] viewDidLoad A
2020-09-01 14:35:32.950182+0800 LifecycleDemo[36547:1782572] viewWillAppear A
2020-09-01 14:35:32.951681+0800 LifecycleDemo[36547:1782572] viewWillLayoutSubviews A
2020-09-01 14:35:32.951773+0800 LifecycleDemo[36547:1782572] viewDidLayoutSubviews A
2020-09-01 14:35:32.990599+0800 LifecycleDemo[36547:1782572] viewDidAppear A
# A replace to B
01 14:36:14.035390+0800 LifecycleDemo[36547:1782572] loadView B
2020-09-01 14:36:14.035499+0800 LifecycleDemo[36547:1782572] viewDidLoad B
2020-09-01 14:36:14.038010+0800 LifecycleDemo[36547:1782572] viewWillAppear B
2020-09-01 14:36:14.039963+0800 LifecycleDemo[36547:1782572] viewWillLayoutSubviews B
2020-09-01 14:36:14.040172+0800 LifecycleDemo[36547:1782572] viewDidLayoutSubviews B
2020-09-01 14:36:14.041020+0800 LifecycleDemo[36547:1782572] viewWillLayoutSubviews B
2020-09-01 14:36:14.041120+0800 LifecycleDemo[36547:1782572] viewDidLayoutSubviews B
2020-09-01 14:36:14.547319+0800 LifecycleDemo[36547:1782572] viewDidAppear B
# B pop
2020-09-01 14:36:48.190346+0800 LifecycleDemo[36547:1782572] viewWillDisappear B
2020-09-01 14:36:49.063327+0800 LifecycleDemo[36547:1782572] viewDidDisappear B
2020-09-01 14:36:49.063679+0800 LifecycleDemo[36547:1782572] dealloc B
总结
- 针对 replace 的情况,展示 B 的时候不会调用 A 的
viewWillDisappear和viewDidDisappear
方法 - B 其实是悬浮在 A 之上的
# A modally B , B pop
使用 modaly 的方式展示,生命周期调用次序如下:
# A展示
2020-09-01 14:45:21.487420+0800 LifecycleDemo[37619:1802939] loadView A
2020-09-01 14:45:21.487541+0800 LifecycleDemo[37619:1802939] viewDidLoad A
2020-09-01 14:45:21.487808+0800 LifecycleDemo[37619:1802939] viewWillAppear A
2020-09-01 14:45:21.488963+0800 LifecycleDemo[37619:1802939] viewWillLayoutSubviews A
2020-09-01 14:45:21.489044+0800 LifecycleDemo[37619:1802939] viewDidLayoutSubviews A
2020-09-01 14:45:21.519922+0800 LifecycleDemo[37619:1802939] viewDidAppear A
# A model B
2020-09-01 14:46:10.633829+0800 LifecycleDemo[37619:1802939] loadView B
2020-09-01 14:46:10.633947+0800 LifecycleDemo[37619:1802939] viewDidLoad B
2020-09-01 14:46:10.636299+0800 LifecycleDemo[37619:1802939] viewWillAppear B
2020-09-01 14:46:10.638101+0800 LifecycleDemo[37619:1802939] viewWillLayoutSubviews B
2020-09-01 14:46:10.638310+0800 LifecycleDemo[37619:1802939] viewDidLayoutSubviews B
2020-09-01 14:46:10.639080+0800 LifecycleDemo[37619:1802939] viewWillLayoutSubviews B
2020-09-01 14:46:10.639183+0800 LifecycleDemo[37619:1802939] viewDidLayoutSubviews B
2020-09-01 14:46:11.145852+0800 LifecycleDemo[37619:1802939] viewDidAppear B
# B pop
2020-09-01 14:46:48.883707+0800 LifecycleDemo[37619:1802939] viewWillDisappear B
2020-09-01 14:46:49.635664+0800 LifecycleDemo[37619:1802939] viewDidDisappear B
2020-09-01 14:46:49.636051+0800 LifecycleDemo[37619:1802939] dealloc B
你会发现生命周期调用次序和 replace 是一致的
# A popover B , B pop
此处直接给出结论:生命周期调用次序和 replace 是一致的
# 父视图控制 A,B
- (IBAction)showAHideB:(id)sender {
[self.ctrB.view removeFromSuperview];
[self.uiView addSubview:self.ctrA.view];
}
- (IBAction)showBHideA:(id)sender {
[self.ctrA.view removeFromSuperview];
[self.uiView addSubview:self.ctrB.view];
}
# Show A hide B
2020-09-01 15:07:46.351005+0800 LifeCycleWIthReplace[39966:1840958] loadView B
2020-09-01 15:07:46.351191+0800 LifeCycleWIthReplace[39966:1840958] viewDidLoad B
2020-09-01 15:07:46.351518+0800 LifeCycleWIthReplace[39966:1840958] loadView A
2020-09-01 15:07:46.351630+0800 LifeCycleWIthReplace[39966:1840958] viewDidLoad A
2020-09-01 15:07:46.351846+0800 LifeCycleWIthReplace[39966:1840958] viewWillAppear A
2020-09-01 15:07:46.352730+0800 LifeCycleWIthReplace[39966:1840958] viewWillLayoutSubviews A
2020-09-01 15:07:46.352822+0800 LifeCycleWIthReplace[39966:1840958] viewDidLayoutSubviews A
2020-09-01 15:07:46.353644+0800 LifeCycleWIthReplace[39966:1840958] viewDidAppear A
# Show B hide A
2020-09-01 15:08:37.408207+0800 LifeCycleWIthReplace[39966:1840958] viewWillDisappear A
2020-09-01 15:08:37.408460+0800 LifeCycleWIthReplace[39966:1840958] viewWillAppear B
2020-09-01 15:08:37.409167+0800 LifeCycleWIthReplace[39966:1840958] viewWillLayoutSubviews B
2020-09-01 15:08:37.409271+0800 LifeCycleWIthReplace[39966:1840958] viewDidLayoutSubviews B
2020-09-01 15:08:37.409833+0800 LifeCycleWIthReplace[39966:1840958] viewDidDisappear A
2020-09-01 15:08:37.409953+0800 LifeCycleWIthReplace[39966:1840958] viewDidAppear B
# 再次 Show A hide B
2020-09-01 15:09:10.473537+0800 LifeCycleWIthReplace[39966:1840958] viewWillDisappear B
2020-09-01 15:09:10.473771+0800 LifeCycleWIthReplace[39966:1840958] viewWillAppear A
2020-09-01 15:09:10.474719+0800 LifeCycleWIthReplace[39966:1840958] viewDidDisappear B
2020-09-01 15:09:10.474891+0800 LifeCycleWIthReplace[39966:1840958] viewDidAppear A
总结
- 在展示 A 之前使用
[self.ctrB.view removeFromSuperview]
的方式将 B 移除视图,会触发 B 的 view 初始化,从而会调用loadView
和viewDidLoad
; - 其余的情况和 segue 的 show 的方式是一直的
# 总结
在当前页面展示前会分别调用前一个页面的
viewWillDisAppear
和viewDidDisAppear
有:使用自定义
addSubView
的方式;使用 segue 的
show
模式: 在NavigationController
存在的情况下,segue
连接的Controller
会被压入导航栈。新压入的视图控制器有返回按钮,单击可以返回;使用 NavigationController 的 pushViewController和 popToViewController 方式
A -> B 不会触发 A 的
viewWillDisappear和viewDidDisappear
方法使用 segue 的
show Detail
模式: 这种类型是不压栈的,不管有没有NavigationController
,它只是replace
取代了当前的视图,不提供返回按钮。使用 segue 的
Present Modally
模式: 这种类型是不压栈的,以模态的方式显示,类似于弹出的警告窗口、登陆框一类的视图;用户无法与上一个视图交互,除非关闭当前视图。使用 segue 的
Present As Popover
模式: 这种类型不压栈,类似于下拉菜单;在 iPad 中,目标视图以浮动窗样式呈现,点击目标视图以外区域,目标视图消失;在 iPhone 中,默认目标视图以模态覆盖屏幕使用代码的方式 presentViewController
- 本文链接: https://mrgaogang.github.io/ios/life/%E6%B5%85%E8%B0%88ViewController%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 许可协议。转载请注明出处!