UIViewController 生命周期的一点思考和实践

96
liangdahong
2017.10.11 10:33* 字数 420

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-4log都比较好理解。对5-8的执行顺序如果不真实测试就不清楚了。测试后为如上的执行顺序,那么为什么是这样的顺序呢?为什么就不可以是

...
6. B viewWillAppear     B将要显示
5. A viewWillDisappear  A将要消失
8. B viewDidAppear      B已经显示
7. A viewDidDisappear   A已经消失

开始笔者也百思不得其解,然后去群里问了下各种大佬。

  • 测试了不知道了
  • 你当前的不处理好,怎么能让你要push的vc做别的事
  • ...
  • 只能是呵呵了。

具体是为什么呢?通过使用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已经消失
iOS开发