文章目录

  • UIViewController的生命周期
  • UIView的生命周期
  • UIViewController和UIView进入展示时整体生命周期调用顺序
  • 开发技巧总结

UIViewController的生命周期

  • UIViewController生命周期相关函数:
// 类的初始化方法
+ (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;

  • 如果是通过Storyboard的Segue方式addChildViewController或者push将要展示的ViewController,UIViewController中生命周期相关函数调用顺序如下:

  • pop 弹出,消失销毁时,UIViewController中生命周期相关函数调用顺序如下:


  • init,push展示时调用顺序:
  • pop时调用顺序:

  • initWithNibName,通过xib初始化控制器并push展示时调用顺序:

  • pop时调用顺序:


  • 由打印结果可以看出,当加载展示ViewController时,三种方式调用是生命周期方法顺序大致相同,但是有些方法调用的还是有些不同的:

    1. 通过Storyboard创建控制器并通过Segue方式展示控制器时,会调用- (instancetype)initWithCoder:(NSCoder *)coder;-(void)awakeFromNib;方法,不会调用- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil ;- (instancetype)init;方法;
    2. init方法初始控制器并push展示控制器,会调用- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil ;- (instancetype)init;方法;不会调用- (instancetype)initWithCoder:(NSCoder *)coder;-(void)awakeFromNib;方法
    3. initWithNibName初始化控制器并push展示,会调用- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil,不会调用- (instancetype)init;,- (instancetype)initWithCoder:(NSCoder *)coder;-(void)awakeFromNib;方法.
    4. 除此以外其他生命周期方法都会调用,并且调用顺序相同.
  • pop弹出返回上个控制器,当前控制器消失销毁时,三种方式调用的生命周期方法是一样的.

  • 值得注意的是当A控制器的导航控制器pushB控制器进来显示时,调用的顺序为:B的viewDidLoad,A的viewWillDisappear,B的viewWillAppear,A的viewDidDisappear,B的viewDidAppear;当B的导航控制器pop当前控制器,返回展示A控制器时,调用顺序:B的viewWillDisappear,A的viewWillAppear,B的viewDidDisappear,A的viewDidAppear,B的dealloc.

  • viewWillLayoutSubviewsviewDidLayoutSubviews方法会在viewWillAppearviewDidAppear之间调用,并且可能会被调用多次.

  • 上面打印顺序都是基于不去变动self.view的情况下,如果是initWithCoder归档的方式初始化控制器,在方法内访问self.viewgetter方法,会调用[self loadview]方法,此时会坏内存访问报错Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0),但是可以在次创建初始化self.view,设置frame并不影响最终展示的位置和大小,但可以设置其他属性如背景颜色,并且该创建的view会将归档中的视图替换掉.一点创建了self.view,会优先调用viewDidLoad方法,然后调用awakeFromNib,并且不再调用loadView方法.awakeFromNib方法中默认也并没有创建self.view,如果在awakeFromNib方法中访问self.viewgetter方法也会调用[self loadview]方法,但不会报错,说明在此起价已经初始化了self.view,然后在loadview中去加载self.view;

- (instancetype)initWithCoder:(NSCoder *)aDecoder {if (self = [super initWithCoder:aDecoder]) {NSLog(@"%@: %s",NSStringFromClass([self class]) ,__func__);self.view = [UIView new];self.view.frame = CGRectMake(0, 88, 100, 200);self.view.backgroundColor = UIColor.greenColor;}return self;
}

  • init方法初始化控制器时,如果在initWithNibName中访问 self.view的gettter方法,会先调用loadViewviewDidLoad方法,然后才调用init方法;如果是在init方法或者initWithNibName方法手动初始化了self.view,则不再调用loadViewviewDidLoad方法;
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {NSLog(@"%@: %s",NSStringFromClass([self class]) ,__func__);self.view.backgroundColor = UIColor.greenColor;}return self;
}

  • 总的来说,默认会在loadView中通过懒加载的方式加载self.view,如果在之前的方法中访问self.view的getter方法会触发调用loadView方法,如果重写初始化self.view的方法(getter方法).则不会再调用loadView方法;如果是加载系统的self.view,重写时需要调用[super loadView],如果是自定义的self.view(自己创建的),则不需要调用[super loadView].
  • 控制器的self.view的superview是在viewWillAppear添加的,window是在viewWillLayoutSubviews添加的;如下图:

UIView的生命周期

  • UIView生命周期相关函数:
//构造方法,初始化时调用,不会调用init方法
- (instancetype)initWithFrame:(CGRect)frame;
//添加子控件时调用
- (void)didAddSubview:(UIView *)subview ;
//构造方法,内部会调用initWithFrame方法
- (instancetype)init;
//xib归档初始化视图后调用,如果xib中添加了子控件会在didAddSubview方法调用后调用
- (instancetype)initWithCoder:(NSCoder *)aDecoder;
//唤醒xib,可以布局子控件
- (void)awakeFromNib;
//父视图将要更改为指定的父视图,当前视图被添加到父视图时调用
- (void)willMoveToSuperview:(UIView *)newSuperview;
//父视图已更改
- (void)didMoveToSuperview;
//其窗口对象将要更改
- (void)willMoveToWindow:(UIWindow *)newWindow;
//窗口对象已经更改
- (void)didMoveToWindow;
//布局子控件
- (void)layoutSubviews;
//绘制视图
- (void)drawRect:(CGRect)rect;
//从父控件中移除
- (void)removeFromSuperview;
//销毁
- (void)dealloc;
//将要移除子控件
- (void)willRemoveSubview:(UIView *)subview;

  • init方式创建视图,并添加到父视图中展示,不添加子控件,其生命周期调用顺序为:

  • initWithFrame方式创建视图,并添加到父视图中展示,不添加子控件,其生命周期调用顺序为:

  • 移除销毁是调用顺序:

  • 添加子控件展示时调用顺序:

  • 添加子控件移除销毁时调用顺序:


  • xib创建初始化视图,xib中不添加子控件时调用顺序:
  • xib创建初始化视图,xib中添加子控件时调用顺序:

  • 移除销毁时调用顺序,有子控件:

  • UIView生命周期总结:

    1. init方法初始化视图,内部会调用initWithFrame方法,不会调用initWithCoderawakeFromNib方法;
    2. xib归档创建视图会触发initWithCoderawakeFromNib方法,不再调用initinitWithFrame方法;
    3. 添加视图调用addSubview方法会触发didAddSubview犯法.
    4. willMoveToSuperviewdidMoveToSuperview方法,父类变化时,无论是添加到父视图还是移除父视图都会调用;在添加时willMoveToSuperviewnewSuperview即为要将要添加的父视图,此时视图的superview为nil,当执行didMoveToSuperview时,视图的父视图不再为nil,为刚添加的newSuperview;移除时willMoveToSuperviewnewSuperviewnil,didMoveToSuperview时,视图的父视图也为nil;
    5. willMoveToWindowdidMoveToWindow方法,是持有的window变化时调用,无论是添加 到窗口显示还是移除窗口消失销毁都会调用;当添加时willMoveToWindownewWindow参数不为nil,self.window却为nil,didMoveToWindowself.window即为newWindow;移除时willMoveToWindownewWindow参数为nil,didMoveToWindowself.window也为nil,具体见下图:
    6. 也就是说在添加到父视图时会在willMoveToSuperview方法中将视图添加到父视图中,在willMoveToWindow添加到window中才能展示,而在移除时在willMoveToWindow将当前持有的windownil,再在willMoveToSuperview中将父视图置nil;
    7. 在调用removeFromSuperview方法时,其superview已经设置为nil,willRemoveSubview方法在dealloc方法之后调用,但是此时self并没有销毁,并不为nil,应该是runloop还没有切换导致的(还未验证).

UIViewController和UIView进入展示时整体生命周期调用顺序

  • self.view是控制器的默认视图,NoXibView是添加到self.view的子视图,UIViewController和UIView加载展示时整体生命周期调用顺序如下(红色为打印的生命周期方法,蓝色的为superview和window):

  1. vc(控制器)的viewDidLoad方法;
  2. NoXibViewwillMoveToSuperviewdidMoveToSuperview,此时已将NoXibView添加到父视图上;
  3. vc的viewWillDisappear方法,促使vc的view的superview和window还是为nil;
  4. NoXibViewwillMoveToWindowdidMoveToWindow,此时已将NoXibView添加到widonw;
  5. vc的viewWillLayoutSubviews ,此时vcviewsuperviewwindow不再为nil;
  6. vc的viewDidLayoutSubviews ;
  7. NoXibViewlayoutSubviews;
  8. vc的viewDidAppear .
  • UIViewController和UIView消失销毁时整体生命周期调用顺序如下:

  1. vc的viewWillDisappear;
  2. NoXibViewwillMoveToWindow,didMoveToWindow,此时NoXibView的window置nil;
  3. vc的viewDidDisappear;此时vc的view的window和superview都置为nil;
  4. vc的’dealloc’;
  5. NoXibViewwillMoveToSuperviewdidMoveToSuperview,此时NoXibViewsuperview置nil;
  6. NoXibViewremoveFromSuperview;
  7. NoXibView的’dealloc’;
  8. NoXibView的’willRemoveSubview’.
  • 总而言之加载展示时:就是先将子控件添加到当前控件中,再将子控件添加到window,然后将当前控件添加到父控件中,再将当前控件添加到window中;移除销毁时:将当前空前的window置nil,再将父控件置nil,将当前控件移除销毁,再将子控件的window置nil,子控件的父控件置nil;(展示时先添加子控件再添加当前控件再添加父控件,移除时先移除父控件,再移除当前控件,再移除子控件).

开发技巧总结

  • 在开发中很多方法是用不到的,所以也没必要都记顺序,上面之所以要打印superview和window,是因为不管是autoresize还是autolayout布局都是基于父视图的,wiondw是最终的显示载体;
  • 对于UIViewController,viewDidLoad:方法是视图加载完成,我们可以在里面添加子视图,设置相关属性和布局等,所有和self.view相关的操作在该方法中添加比较合适;viewWillAppear:视图即将显示时调用;viewWillLayoutSubviews:视图将要布局其子视图时被调用;viewDidLaySubviews:视图布局完成其子视图时被调用;viewDidAppear视图显示后被调用;viewWillDisappear:视图将要消失时调用;viewDidDisappear:视图已经消失时调用;
  • 注意:viewWillAppear:viewWillDisappear:有时要成对使用;viewDidAppearviewDidDisappear:有时要成对使用,比如显示该页面定位,消失时结束定位;还比如该页面显示时影藏导航,去其他页面时恢复导航;
  • UIViewlayoutSubviews会被经常调用,下面说下layoutsubview的调用情况:
    1、addSubview会触发layoutSubviews,如果addSubview 如果连续2个 只会执行一次,具体原因下面说。
    2、设置view的Frame会触发layoutSubviews,必须是frame的值设置前后发生了变化。
    3、滚动一个UIScrollView会触发layoutSubviews。
    4、旋转Screen会触发父UIView上的layoutSubviews事件。
    5、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。
  • 如果要立即执行layoutsubview ,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局.其中的原理是:执行setNeedsLayout后会在receiver标上一个需要被重新布局的标记,在系统runloop的下一个周期自动调用layoutSubviews。这样刷新会产生延迟,所以我们需要马上执行layoutIfNeeded。就会开始遍历subviews的链,判断该receiver是否需要layout。如果需要立即执行layoutsubview.
  • 每一个视图只能有唯一的一个父视图。如果当前操作视图已经有另外的一个父视图,则addsubview的操作会把它先从上一个父视图中移除(包括响应者链),再加到新的父视图上面。
  • 并且连续2次的addSubview,只会执行一次layoutsubview。因为一次的runLoop结束后,如果有需要刷新,执行一次即可

适用于iOS的View Controller编程指南
UIView - 生命周期

iOS UIViewController和UIView的生命周期相关推荐

  1. iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(上)

    2017-07-08 remember17 Cocoa开发者社区 目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 ...

  2. UIView的生命周期

    UIView对象一般都是由alloc和init方法创建的,由UIViewController进行维护和管理.一般需要完成的任务有:创建视图,显示视图,隐藏视图和释放视图. 一般视图不会在程序运行时立刻 ...

  3. IOS 中视图控制器的生命周期

    1.init Allocating critical data structures required by your view controller 不要出现创建view的代码.良好的设计,在ini ...

  4. iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(下)

    2017-07-08 remember17 Cocoa开发者社区 7NSOperation的理解与使用 No.1:NSOperation简介 NSOperation是基于GCD之上的更高一层封装,NS ...

  5. IOS App的生命周期

    文章目录 主函数main 应用程序的状态 应用程序的启动过程 应用程序从前台切换到后台 应用程序从后台切换到前台 响应中断 锁屏/解锁 app的生命周期和viewcontroller的生命周期 App ...

  6. iOS应用程序生命周期(前后台切换,应用的各种状态)详解

    2019独角兽企业重金招聘Python工程师标准>>> iOS的应用程序的生命周期,还有程序是运行在前台还是后台,应用程序各个状态的变换,这些对于开发者来说都是很重要的. iOS系统 ...

  7. 转:iOS应用程序生命周期(前后台切换,应用的各种状态)详解

    iOS应用程序生命周期(前后台切换,应用的各种状态)详解 分类: iOS开发进阶2012-10-08 15:35 42691人阅读 评论(30) 收藏 举报 iosapplication任务anima ...

  8. 详解 Flutter State 生命周期

    一.说明 我们在开发 Android 或 iOS 中经常会用到页面的生命周期. Android Activity 生命周期: onCreate onStart onResume onPause onS ...

  9. iOS对UIViewController生命周期和属性方法的解析

    iOS对UIViewController生命周期和属性方法的解析 一.引言 作为MVC设计模式中的C,Controller一直扮演着项目开发中最重要的角色,它是视图和数据的桥梁,通过它的管理,将数据有 ...

最新文章

  1. header+php+xiazai_php使用header函数实现文件下载
  2. 二分查找法(递归与循环实现)
  3. 阿里云神龙团队拿下TPCx-BB排名第一的背后技术
  4. element-ui省市区三级联动:选择即改变
  5. Java 反射 set get
  6. 95-38-045-Buffer-UnpooledByteBuf
  7. ACM做题过程中的一些小技巧
  8. 写博客是一种乐趣,一种需要培养的乐趣,Java程序员最大的悲哀是什么
  9. 使用vmware12安装Ubuntu 遇到的两个问题和解决
  10. 人工智能在麻将领域能够战胜人类吗?
  11. 矩阵方程求解最快c语言算法,求助! C语言用矩阵求解方程组
  12. parallel(parallelStream)并发问题
  13. 从eoeandroid换到CSDN-[回顾]
  14. 12306 登录验证数据下载(未标定)
  15. 案例|高稳定紫外LED光源助力流体力学PSP技术
  16. c语言中的EOF是什么意思
  17. 来,看看记事本里会变成乱码的字……不仅仅是“联通”而已……
  18. AM437x——LED驱动
  19. 操作系统 文件换行符问题
  20. [渝粤教育] 西南科技大学 高级英语(1) 在线考试复习资料

热门文章

  1. ie8支持css吗,教你如何轻松解决css不兼容IE8
  2. WD backup西部盘数据备份
  3. css实现背景透明,内容不透明
  4. JavaScript进阶班之DOM技术(四)
  5. hp ProBook 4441s 修复记
  6. 剑网3云南玩哪个服务器延迟低,为何《剑网3》玩家黏度高?大神道出原因,根本不用下其他游戏...
  7. Oculus Rift S丨(三)实现Oculus空间出现手的效果、隔空抓取
  8. javascript大作业《web课程设计》用html做一个期末作业网站,梅西足球体育网页,css...
  9. 比较火的计算机应用软件,6个好用但不火的软件,总有一款你用得到
  10. git初始化错误fatal: refusing to merge unrelated histories