View的声明周期详解

  • iOS控制器平时总调用viewDidload其流程也是从 viewDidLoad->viewWillAppear -> viewDidAppear ->viewWillDisappear -> viewDidDisappear基本的流程就这样了,那还有没有呢???答案是当然有具体是什么呢?下面具体来研究下,小知识也能翻船😭
  • 先看看API吧😭
- (void)loadView; // This is where subclasses should create their custom view hierarchy if they aren't using a nib. Should never be called directly.
- (void)loadViewIfNeeded NS_AVAILABLE_IOS(9_0); // Loads the view controller's view if it has not already been set.
- (void)viewWillUnload NS_DEPRECATED_IOS(5_0,6_0) __TVOS_PROHIBITED;
- (void)viewDidUnload NS_DEPRECATED_IOS(3_0,6_0) __TVOS_PROHIBITED; // Called after the view controller's view is released and set to nil. For example, a memory warning which causes the view to be purged. Not invoked as a result of -dealloc.

- (void)viewDidLoad; // Called after the view has been loaded. For view controllers created in code, this is after -loadView. For view controllers unarchived from a nib, this is after the view is set.
- (BOOL)isViewLoaded NS_AVAILABLE_IOS(3_0);
- (void)viewWillAppear:(BOOL)animated;    // Called when the view is about to made visible. Default does nothing
- (void)viewDidAppear:(BOOL)animated;     // Called when the view has been fully transitioned onto the screen. Default does nothing
- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
- (void)viewDidDisappear:(BOOL)animated;  // Called after the view was dismissed, covered or otherwise hidden. Default does nothing

// Called just before the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. The default is a nop.
- (void)viewWillLayoutSubviews NS_AVAILABLE_IOS(5_0);
// Called just after the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. The default is a nop.
- (void)viewDidLayoutSubviews NS_AVAILABLE_IOS(5_0);
- (void)didReceiveMemoryWarning; // Called when the parent application receives a memory warning. On iOS 6.0 it will no longer clear the view by default.

看到这里很惭愧,里面很多方法平时很少用,那么具体的流程到底是怎样的呢?
自己写执行代码打印输入如下:
从A到B

viewDidLoad-----A
viewWillAppear-----A
viewWillLayoutSubviews-----A
viewDidLayoutSubviews-----A
viewDidAppear-----A
viewDidLoad-----B
viewWillDisappear-----A
viewWillAppear-----B
viewWillLayoutSubviews-----B
viewDidLayoutSubviews-----B
viewDidDisappear-----A
viewDidAppear-----B

  • AB的过程中,B加载后(即viewDidLoad->B)此时A将会消失,等到B视图出现之前A彻底视图消失
    从B到A

viewWillDisappear-----B
viewWillAppear-----A
viewDidDisappear-----B
viewDidAppear-----A

  • BA的过程中,首先B视图将会消失,等到A视图彻底出现之前,B视图才会彻底消失.
  • 再从AB

viewDidLoad-----B
viewWillDisappear-----A
viewWillAppear-----B
viewWillLayoutSubviews-----B
viewDidLayoutSubviews-----B
viewDidDisappear-----A
viewDidAppear-----B

  1. loadView:每次访问view时,就会调用self.view的get方法,在get方法中判断self.view==nil.不为nil就直接返回view,等于nil就去调用loadView方法。loadView方法会去判断有无指定storyboard/Xib文件,如果有就去加载storyboard/Xib描述的控制器View,如果没有则系统默认创建一个空的view,赋给self.view。loadView方法有可能被调用多次(每当访问self.view并且为nil时就会调用一次);
  2. viewDidLoad:view加载完成时调用(在loadView方法执行后调用)也有可能执行多次(self.view==nil且被访问时)
  3. awakeFromNib:通过storyboard创建控制器加载了控制器以及控制器view的nib文件,此方法在initWithCoder调用后被调用。而通过Xib创建控制器只加载了控制器view的nib文件,所以控制器的awakeFromNib方法不会被调用
    awakeFromNib方法是在将统一归档中的所有对象都读取并初始化完成后才会被调用。
    4.viewWillAppearview即将可见时调用
    5.viewWillLayoutSubViews:view即将布局子视图时调用
    6.viewDidLayoutSubviews:view完成子视图布局后调用
    7.viewDidAppear:view已经显示后调用
    8.viewWillDisappear:view即将消失、被覆盖或者隐藏事调用此方法
    9.viewDidDisappear:view已经消失、被覆盖或者隐藏时调用此方法
    10.didReceiveMemoryWarning:当收到内存警告时调用此方法
    11.viewWillUnload:当内存过低时,需要释放一些不需要使用的视图时,即将释放时调用(iOS6以后被废弃)
    12.viewDidUnload:当内存过低,释放了一些不需要的视图时调用(iOS6以后被废弃)
    总结如下图所示
    view的声明周期图
内存警告时view的处理机制
  • iOS6以前(不包含iOS6)当内存警告时,我们会在viewDidUnload中手动回收viewController的子视图或者ViewControllerview([self.view removeFromSuperVIew];self.view = nil;),当view再次被访问到时,就会调用loadView方法,viewControllerview及其子视图会被重新创建。
  • 内存警告时,viewDidUnload一定会被调用
  • loadView会被调用多次
    iOS6以后苹果废弃了viewWillUnloadviewDidUnload方法,所以以前在viewDidUnload中处理内存警告的代码就需要移动到didReceiveMemoryWarning中。但如果你觉得这样就没有问题了,那就错了。iOS6以后苹果废弃了那两个方法的同时也添加了内部处理view的方式,具体的处理机制如下:

iOS6及以后,内存警告时系统会回收ViewController的View的CALayer里的BitMap(CABackingStore类型,它的内容是直接用于渲染到屏幕,它是View消耗内存的大户)。view和calayer占的内存极少, 数量级也就在byte和kbyte之间,所以系统只回收了BitMap,但是这里所谓的回收只是给BitMap占用的内存打了一个volatile标记表明这部分内存是可能随时被其它数据占用,平时没内存警告时正在使用的内存标记为In use,完全被释放回收的标记为Not in use。概括起来也就是说:iOS6及以后的内存警告时,系统会给用于渲染视图的数据(BitMap)内存打一个volatile, ViewController的View的架子结构并不会回收,当View再次被访问时,虽然View的架子结构会用重建,但触发drawRect来渲染界面时,如果view对应的BitMap数据内存没有被占用则会被View的drawRect方法直接渲染出来且内存被标记为in use,从而这块内存又可以独享了;如果已被其它数据占用,那么BitMap必须要重建。所以可以看到整个重建过程不再是由loadView来做的,它是通过对view的访问来触发的。但是,请注意, 如果说在iOS6及以后ViewController的loadView方法只会被调用一次,这种说法是不完全准确的。因为:如果在didReceiveMemoryWarning里把ViewController的View也回收了([self.view removeFromSuperview];self.view = nil;),那么当再次有对View访问时,loadView会被调用以进行完全最彻底的重建(想想也是,ViewController的View都没了,不调loadView来重建那怎么办呢)。

iOS6这种的设计的优点:

  • 视图结构和视图数据的分离;
  • 内存警告后系统只回收的是内存大户视图数据,但是回收不是完全的清掉,而只是做个标记,这样既做到减小了每次重建BitMap 的成本,同时也把这部分内存开放出去可以随时被别的数据占用;
  • 重建时,充其量是重建BitMap(没被占用时是直接用不用重建)
写作不易,如有错误欢迎大家指正,如果喜欢请给个小❤️❤️

推荐阅读更多精彩内容