UIView和VC在多种情况下,生命周期变化。以及VC和 UIView的生命周期函数详解。
vc的上生命周期及理解
打开VC
init或initWithCoder和initWithNibName:bundle:
initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib)
如果使用 StoryBoard 进行视图管理,程序不会直接初始化一个 UIViewController,StoryBoard 会自动初始化或在 segue 被触发时自动初始化,因此方法initWithNibName:bundle 不会被调用,但是 initWithCoder 会被调用。如果不用StoryBoard,则调用initWithNibName:bundle:
loadView
创建或加载一个 view 并把它赋值给 UIViewController 的 view 属性
当执行到 loadView 方法时,如果视图控制器是通过 nib 创建,那么视图控制器已经从 nib 文件中被解档并创建好了,接下来任务就是对 view 进行初始化。loadView 方法在 UIViewController 对象的 view 被访问且为空的时候调用。这是它与 awakeFromNib 方法的一个区别。
假设我们在处理内存警告时释放 view 属性:self.view = nil。因此 loadView 方法在视图控制器的生命周期内可能被调用多次。loadView 方法不应该直接被调用,而是由系统调用。它会加载或创建一个 view 并把它赋值给 UIViewController 的 view 属性。
在创建 view 的过程中,首先会根据 nibName 去找对应的 nib 文件然后加载。如果 nibName 为空或找不到对应的 nib 文件,则会创建一个空视图(这种情况一般是纯代码)
注意:在重写 loadView 方法的时候,不要调用父类的方法。
viewDidLoad
视图加载完成后调用,此时视图控制器基本工程已初始化完成,一般将一些 Controller 额外定义功能的初始化工作放在此函数中。
当 loadView 将 view 载入内存中,会进一步调用 viewDidLoad 方法来进行进一步设置。此时,视图层次已经放到内存中,通常,我们对于各种初始化数据的载入,初始设定、修改约束、移除视图等很多操作都可以这个方法中实现。
视图层次(view hierachy):因为每个视图都有自己的子视图,这个视图层次其实也可以理解为一颗树状的数据结构。而树的根节点,也就是根视图(root view),在 UIViewController 中以 view 属性。它可以看做是其他所有子视图的容器,也就是根节点。
viewWillAppear
- (void)viewWillAppear:(BOOL)animated
animated:如果为YES,则使用动画将视图添加到窗口。
在将视图控制器的视图添加到视图层次结构之前,以及配置任何动画以显示视图之前,将调用此方法。您可以重写此方法以执行与显示视图关联的自定义任务。例如,您可能使用此方法来更改状态栏的方向或样式以与所显示视图的方向或样式相协调。如果重写此方法,则必须super在实现中的某个时刻调用。
viewWillLayoutSubviews
视图将要布局其子视图时被调用。
view 即将布局其 Subviews。 比如 view 的 bounds 改变了(例如:状态栏从不显示到显示,视图方向变化),要调整 Subviews 的位置,在调整之前要做的工作可以放在该方法中实现。
viewDidLayoutSubviews
视图布局完成其子视图时被调用。
view 已经布局其 Subviews,这里可以放置调整完成之后需要做的工作。
viewDidAppear
- (void)viewDidAppear:(BOOL)animated
视图已经展示在屏幕上,可以对视图做一些关于展示效果方面的修改。
在 view 被添加到视图层级中以及多视图,上下级视图切换时调用这个方法,在这里可以对正在显示的视图做进一步的设置
退出VC
viewWillDisappear
- (void)viewDidDisappear:(BOOL)animated
在试图将要消失时调用,做一些数据保存和清理工作。
此时还没有调用 removeFromSuperview。
viewDidDisappear
- (void)viewWillDisappear:(BOOL)animated
视图已经消失时调用。
view 已经消失或被覆盖,此时已经调用 removeFromSuperView;
dealloc
对象的销毁方法,在对象释放时调用,可通过在其中打印信息检查是否存在内存泄露等问题
您可以重写此方法来处理对象实例变量以外的资源。
在的实现中dealloc,请勿调用超类的实现。您应该尝试避免使用来管理有限资源(例如文件描述符)的生命周期dealloc。
您永远不会dealloc直接发送消息。而是dealloc由运行时调用对象的方法。
其他
didReceiveMemoryWarning
在内存足够的情况下,APP 的视图通常会一直保存在内存中,但是如果内存不够,一些没有正在显示的 viewController 就会收到内存不足的警告,然后就会释放自己拥有的视图,以达到释放内存的目的。但是系统只会释放内存,并不会释放对象的所有权,所以通常我们需要在这里将不需要显示在内存中保留的对象释放它的所有权,将其指针置 nil。
UIView的生命周期及其理解
initWithFrame 和init
初始化函数,调用init函数会调用initWithFrame,此时传入CGRectZero。所以一般用initWithFrame来初始化,并且不要用其中传入的 CGRect。
willMoveToSuperview
- (void)willMoveToSuperview:(UIView *)newSuperview
告诉视图其父视图即将更改为指定的超级视图。
进入或者离开父view。在加到superView上时newSuperview为父view,从父view上移除时 newSuperview =nil
此方法的默认实现不执行任何操作。每当父视图更改时,子类都可以覆盖它以执行其他操作。
didMoveToSuperview
当view的父布局已经发生改变时调用,如果为superview变成nil,说明从父视图上移除
此方法的默认实现不执行任何操作。每当父视图更改时,子类都可以覆盖它以执行其他操作。
willMoveToWindow
- (void)willMoveToWindow:(nullable UIWindow *)newWindow
告诉视图其窗口对象即将更改
进入或者离开window前调用。 在移动到newWindow上时newWindow为父展示的window,从window上移除时 newWindow =nil
此方法的默认实现不执行任何操作
didMoveToWindow
告诉视图其窗口对象已更改
进入或者离开window前调用。此方法的默认实现不执行任何操作
该window属性可能是nil在调用此方法时出现的,指示接收者当前不在任何窗口中。当接收器刚刚从其超级视图中删除时,或者当接收器刚刚添加到未附加到窗口的超级视图中时,就会发生这种情况。如果不感兴趣,则此方法的替代可能会选择忽略这些情况。
layoutSubviews
调整子view布局位置
该函数默认实现将使用您设置的任何约束来确定任何子视图的大小和位置。
子类可以根据需要重写此方法,以对其子视图执行更精确的布局。仅当子视图的自动调整大小和基于约束的行为没有提供所需的行为时,才应覆盖此方法。您可以使用实现直接设置子视图的框架矩形。
您不应该直接调用此方法。如果要强制布局更新,请在下一次图形更新之前调用该方法:setNeedsLayout 。如果要立即更新视图的布局,请调用该方法:layoutIfNeeded。
常见自动触发时机:
- frame的改变
- 通过 addsubView 来触发
- 滚动一个UIScrollView会触发layoutSubviews
- 旋转Screen会触发父UIView上的layoutSubviews事件
- 改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件
vc及其上作为根view的生命周期
进入vc
进入vc时,vc的生命周期和根view的生命周期
退出vc
退出vc时,vc的生命周期和根view的生命周期