UIViewController 生命周期的一點思考和實踐
說到UIViewController的生命周期,可能第一時間會想到下面的各種方法
- (instancetype)init;
- (instancetype)initWithCoder:(NSCoder *)aDecoder;
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
- (void)loadView;
- (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.
- (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
- (void)viewWillLayoutSubviews;
- (void)viewDidLayoutSubviews;
- ...
各種方法的執行問題
待續
控制器切換時的執行順序
push
假設現在有一個 AViewController(簡稱 Avc) 和 BViewController (簡稱 Bvc),通過 navigationController 的 push 實現 Avc 到 Bvc 的跳轉,下面是各個方法的執行執行順序:
1. A viewDidLoad A的view已經加載
2. A viewWillAppear A將要顯示
3. A viewDidAppear A已經顯示
4. B viewDidLoad B的view已經加載
5. A viewWillDisappear A將要消失
6. B viewWillAppear B將要顯示
7. A viewDidDisappear A已經消失
8. B viewDidAppear B已經顯示
可能對1-4
的log
都比較好理解。對5-8
的執行順序如果不真實測試就不清楚了。測試后為如上的執行順序,那么為什么是這樣的順序呢?為什么就不可以是
...
6. B viewWillAppear B將要顯示
5. A viewWillDisappear A將要消失
8. B viewDidAppear B已經顯示
7. A viewDidDisappear A已經消失
開始筆者也百思不得其解,然后去群里問了下各種大佬。
-
測試了不知道了
。 - ...
具體是為什么呢?通過使用https://github.com/BigZaphod/Chameleon查看push
的具體源碼可知。
-
UINavigationController.m
中的push方法中調用了 [self _updateVisibleViewController:animated];
- 在
_updateVisibleViewController
的實現中可以看到
[oldVisibleViewController beginAppearanceTransition:NO animated:animated];
[newVisibleViewController beginAppearanceTransition:YES animated:animated];
-
beginAppearanceTransition
的實現如下:
- (void)beginAppearanceTransition:(BOOL)isAppearing animated:(BOOL)animated
{
if (_appearanceTransitionStack == 0 || (_appearanceTransitionStack > 0 && _viewIsAppearing != isAppearing)) {
_appearanceTransitionStack = 1;
_appearanceTransitionIsAnimated = animated;
_viewIsAppearing = isAppearing;
if ([self shouldAutomaticallyForwardAppearanceMethods]) {
for (UIViewController *child in self.childViewControllers) {
if ([child isViewLoaded] && [child.view isDescendantOfView:self.view]) {
[child beginAppearanceTransition:isAppearing animated:animated];
}
}
}
if (_viewIsAppearing) {
[self view]; // ensures the view is loaded before viewWillAppear: happens
[self viewWillAppear:_appearanceTransitionIsAnimated];
} else {
[self viewWillDisappear:_appearanceTransitionIsAnimated];
}
} else {
_appearanceTransitionStack++;
}
}
- 在
_updateVisibleViewController
還有如下的調用
[oldVisibleViewController endAppearanceTransition];
[newVisibleViewController endAppearanceTransition];
-
endAppearanceTransition
的實現如下:
- (void)endAppearanceTransition
{
if (_appearanceTransitionStack > 0) {
_appearanceTransitionStack--;
if (_appearanceTransitionStack == 0) {
if ([self shouldAutomaticallyForwardAppearanceMethods]) {
for (UIViewController *child in self.childViewControllers) {
[child endAppearanceTransition];
}
}
if (_viewIsAppearing) {
[self viewDidAppear:_appearanceTransitionIsAnimated];
} else {
[self viewDidDisappear:_appearanceTransitionIsAnimated];
}
}
}
}
- 如果把上面的調用全部看完后相信你已經知道為什么用先前的執行順序了吧,其實就是 Apple的調用順序問題。【當然也有這樣做的原因】
presentViewController
上面看了 push 時的執行順序,那么我們是否可以看看
presentViewController
的執行順序呢?使用同樣的方法可以清楚的看到。但是執行順序和push不一致。
1. A viewDidLoad A的view已經加載
2. A viewWillAppear A將要顯示
3. A viewDidAppear A已經顯示
4. B viewDidLoad B的view已經加載
5. A viewWillDisappear A將要消失
6. B viewWillAppear B將要顯示
7. B viewDidAppear B已經顯示
8. A viewDidDisappear A已經消失