我们看UIView的层次结构类别中有以下方法:
//view层级操作
- (void)addSubview:(UIView *)view;
- (void)didAddSubview:(UIView *)subview;
- (void)willRemoveSubview:(UIView *)subview;
- (void)willMoveToSuperview:(nullable UIView *)newSuperview;
- (void)didMoveToSuperview;
- (void)willMoveToWindow:(nullable UIWindow *)newWindow;
- (void)didMoveToWindow;
- (void)removeFromSuperview;
//view布局操作
- (void)layoutSubviews;
- (void)setNeedsLayout;
- (void)layoutIfNeeded;
我们看UIView的渲染类别中有以下方法:
- (void)drawRect:(CGRect)rect;
- (void)setNeedsDisplay;
- (void)setNeedsDisplayInRect:(CGRect)rect;
我们重写这些函数,测试执行顺序:
现在view的添加顺序如:[view1 addSubview view12];[view11 addSubview view111];[view1 addSubview view11];[self.view addSubview view1];[self.view addSubview view2];
1.先执行 initWithCoder:/ initWithNibName: bundle:方法
2.执行loadView方法
3.执行 viewDidLoad方法
4.先会调用view1的init方法
5.初始化完成会调用view12的willMoveToSuperview:方法
6.接着调用view12的didMoveToSuperview:方法
7.view1的 didAddSubview:方法
8.接着调用view1的 addSubview:方法
9.按照addSubview:顺序,重复5、6、7、8
10.直到所有的subview全部添加完,会调用一次viewWillAppear:方法
11.接着调用 loadViewIfNeeded方法,表示loadView已加载完毕
12.会到用superview的 willMoveToWindow:方法,再调用subview(view2)的 willMoveToWindow:方法,
13.调用subview的didMoveToWindow方法,再调用superview的 didMoveToWindow方法
14.将加载的所有view绘制到window上之后,会调用 viewWillLayoutSubviews、 viewDidLayoutSubviews方法
15.先调用layoutSubviews布局。布局完成,再调用drawRect:方法
父子view的layoutSubviews、drawRect:的调用顺序稍后在说
16.最后调用viewDidAppear:方法进行展示
页面消失
1.先调用viewWillDisappear:方法
2.将所有view从window上移除:
2.1先到用superview的willMoveToWindow:方法,再调用subview的willMoveToWindow:方法
2.2接着调用subview的didMoveToWindow:方法,再调用superview的didMoveToWindow:方法
3.调用viewDidDisappear:方法
4.调用dealloc方法
5.先superview后subview分别调用willMoveToSuperview:、didMoveToSuperview、removeFromSuperview方法(如果view有subview,会调用willRemoveSubview方法,多个会调用多次)
讨论父子view的willMoveToWindow:、didMoveToWindow:、layoutSubviews、drawRect:调用顺序以及删除时willMoveToSuperview:、didMoveToSuperview、removeFromSuperview的调用顺序
按照view的添加顺序(添加的节点按照从左到右),构成一个树
willMoveToWindow:方法调用顺序:
按照树结构,从左到右遍历,直到该结点的所有字节点遍历完:view1->view12->view11->view111->view2
didMoveToWindow方法调用顺序:
按照树结构,从左到右,先遍历叶子结点,再从叶子结点向上遍历,重复之前过程:view12->view111->view11->view1->view2
以上操作顺序,即保证了每个view可以添加到window上,又保证了子view添加完成,父view才可以添加完成
layoutSubviews、 drawRect调用顺序:
1.按照树结构,从右到左遍历,直到每个节点遍历完:view2->view1->view11->view111->view12
2.保证先布局父view是试图,在布局子view试图
删除时willMoveToSuperview:、didMoveToSuperview、removeFromSuperview的调用顺序:
1.按照树结构,从左到右,先遍历根结点,遍历完根节点,再从右往左遍历根结点的子结点:view1->view2->view12->view11->view111
2.为什么要这么做?确保view删除,view的字节点也删除,即保证每个节点都删除了
注意:
1.willMoveToSuperview:和didMoveToSuperview:方法调用顺序,与调用addSubview:顺序有关。先add谁,谁先调用
2.xxMoveToWindow:、layoutSubviews、drawRect:方法的调用与view的层级有关系
3.删除时,willMoveToSuperview:和didMoveToSuperview:方法调用顺序是先superview,再subview
4.如果我们手动删除一个view,例如 [view removeFromSuperview];
1)调用 willMoveToSuperview
2)调用 willMoveToWindow,再调用subview的willMoveToWindow,subview的didMoveToWindow, didMoveToWindow
3)调用removeFromSuperview
4)调用 viewWillLayoutSubviews、 viewDidLayoutSubviews
由此可见,手动删除,该view会被完全移除,但是它的subview仅仅只是从window上移除,subview并没有从superview上移除,它还在内存中
总结:
UIView生命周期
init->willMoveToSuperview->didMoveSuperview->(如果有子view)->subview的willMoveToSuperview->subview的didMoveSuperview->didAddSubview->addSubview-viewWillAppear->loadViewIfNeeded->willMoveToWindow->(如果有子view)->subview的willMoveToWindow->subview的didMoveToWindow->didMoveToWindow->viewWillLayoutSubviews->viewDidLayoutSubviews->layoutSubviews->drawRect->viewDidAppear
UIViewController生命周期
1.先执行 initWithCoder:/ initWithNibName: bundle:方法
2.loadView
3.viewDidLoad
4.viewWillAppear
5.loadViewIfLayoutNedded
6.viewWillLayoutSubviews
7.viewDidLayoutSubviews
8.viewDidAppear
9.viewWillDisappear
10.viewDidDisappear
11.viewDidUnLoad:这个函数时viewDidLoad的对立函数。在程序内存欠缺时,这个函数被controller调用,来释放他的view以及view相关的对象
12.didReceiveMemoryWarning:当程序内存过度时,系统会调用该方法
13.dealloc