技术咨询、项目合作、广告投放、简历咨询、技术文档下载 点击这里 联系博主

# 浅谈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


注意点:

  1. A push to B 下,B 展示的时候在 BviewWillAppear即将展示前会viewWillDisappear A在 B 展示前 A 会viewDidDisappear
  2. 在 B pop 时会先调用 B 的即将消失viewWillDisappear,然后才会调用 A 的即将展示viewWillAppear

总结

  1. 针对 pop 的情况,被压入堆栈的页面不会触发销毁dealloc,弹出堆栈页面的会触发dealloc;

  2. 对于新打开的页面整体触发流程为: loadView -> viewDidLoad -> viewWillAppear -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear ;

  3. 经过 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

总结

  1. 针对 replace 的情况,展示 B 的时候不会调用 A 的 viewWillDisappear和viewDidDisappear方法
  2. 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


总结

  1. 在展示 A 之前使用[self.ctrB.view removeFromSuperview]的方式将 B 移除视图,会触发 B 的 view 初始化,从而会调用loadViewviewDidLoad;
  2. 其余的情况和 segue 的 show 的方式是一直的

# 总结

  1. 在当前页面展示前会分别调用前一个页面的viewWillDisAppearviewDidDisAppear 有:

    • 使用自定义 addSubView 的方式;

    • 使用 segue 的show模式: 在 NavigationController 存在的情况下,segue 连接的 Controller 会被压入导航栈。新压入的视图控制器有返回按钮,单击可以返回;

    • 使用 NavigationController 的 pushViewController和 popToViewController 方式

  2. A -> B 不会触发 A 的 viewWillDisappear和viewDidDisappear方法

    • 使用 segue 的show Detail模式: 这种类型是不压栈的,不管有没有 NavigationController,它只是 replace 取代了当前的视图,不提供返回按钮。

    • 使用 segue 的Present Modally模式: 这种类型是不压栈的,以模态的方式显示,类似于弹出的警告窗口、登陆框一类的视图;用户无法与上一个视图交互,除非关闭当前视图。

    • 使用 segue 的Present As Popover模式: 这种类型不压栈,类似于下拉菜单;在 iPad 中,目标视图以浮动窗样式呈现,点击目标视图以外区域,目标视图消失;在 iPhone 中,默认目标视图以模态覆盖屏幕

    • 使用代码的方式 presentViewController

【未经作者允许禁止转载】 Last Updated: 7/22/2024, 7:13:58 AM