ios开发之切换RootViewController

版权声明:文章为本人个人总结,如需转载请声明 https://blog.csdn.net/weixin_39624536/article/details/89736125

写在开头

最近做需求开发,在登陆请求返回后需要填写一些基本信息,然后再显示主页面

显示登录界面时创建了RootViewController

显示主页面的时候,以Tabbar作为新的RootViewController

切换RootViewController以后,页面看起来没有什么问题,查看层级关系发现原来的界面并没有释放,造成了内存泄漏

1.pushViewController

push出来的子控制器,导航栏控制器会对该子控制器进行强引用,

Push 其本质就是入栈操作,在入栈的过程中,可能不止是导航栏控制器对子控制器有强引用,其中 push 对应着 pop 则控制器会被销毁。

其push的本质是入栈,所有的子控制器放在数组中,后进先出。

如果导航栏作为根控制器。此时需要更该根控制器,不管push了多少子控制器入栈,其最后只需要在当前显示的子控制器中对根控制器重新赋值即可。

即原先的导航栏控制器没有对象强引用它,它会被释放,其所有的子控制器自然都会被释放了 前提是你本身代码不存在其他内存泄漏

UIApplication.shared.keyWindow.rootViewController = newRootVC

2.presented VC(showMoalVC)

除了push出来的页面,可能还会出现present出来新的页面,这个时候如果在present页面更换RootViewController。看起来是没有问题的,实际上原来的页面还保留着,产生了内存泄漏。

那么,谁来让presented VC消失呢?正确的做法是“谁污染谁治理”。

对于弹出模态,系统指定了销毁的方法dismiss,其中动画效果暂时关闭

- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^ __nullable)(void))completion NS_AVAILABLE_IOS(5_0);
例如:ViewController push ----> firstVC1 present ----> firstVC2 。则如何在firstVC2中切换根控制器,并同时销毁之前的控制器。则必须先dismiss掉 firstVC2 。
[self dismissViewControllerAnimated:NO completion:^{
   UIApplication.shared.keyWindow.rootViewController = newRootVC
}];

分析:如果不dismiss掉 firstVC2,则firstVC2不会被释放。firstVC2无法释放,必然导致firstVC1无法释放,而栈顶元素无法被释放,栈顶下面的控制器都无法释放。

注:

1、dismiss动画状态必须关闭,根控制器的切换与dismisse的动画同时进行会给用户带来较差的体验效果。

2、在项目开发中,只要有present出来的控制器,一定要有对应的dismiss,否则项目中会存在无法估量的bug(会不会有例外呢?)

发现一个例外

下面是我们工程中自己封装的方法

+ (void)showModalNavigationController:(UIViewController *)viewController animated:(BOOL)animated
{
    NSAssert([[NSThread currentThread] isEqual:[NSThread mainThread]], @"PUSH can only be started from the main thread.");
    [[AudioManager sharedInstance] stopPlayAndClearPlayList];
    RootNavViewController *nav = [[RootNavViewController alloc] initWithRootViewController:viewController];
    [AppNavigator showModalViewController:nav animated:animated];
}


+ (void)showModalViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    NSAssert([[NSThread currentThread] isEqual:[NSThread mainThread]], @"PUSH can only be started from the main thread.");

    if ([AppNavigator navigator].mainNav != nil) {
        if ([[AppNavigator navigator].mainNav presentedViewController]) {
            UIViewController *presentCon = [[AppNavigator navigator].mainNav presentedViewController];
            while (1) {
                UIViewController *presentConTmp = [presentCon presentedViewController];
                if (presentConTmp && [presentConTmp isKindOfClass:[UINavigationController class]]) {
                    presentCon = presentConTmp;
                }else{
                    break;
                }
            }
            [presentCon presentViewController:viewController animated:animated completion:nil];
        } else {
            [[AppNavigator navigator].mainNav presentViewController:viewController animated:animated completion:nil];
        }
    }
}

偶然间发现一个事情,当使用如下方法:

+ (void)showModalNavigationController:(UIViewController *)viewController animated:(BOOL)animated

在Present出一个Controller之前,我们先创建了一个RootNavViewController。然后在页面返回时,可以试一下popViewController,会有神奇的效果!

参考文献:https://www.jianshu.com/p/ac4aebdc74d4

猜你喜欢

转载自blog.csdn.net/weixin_39624536/article/details/89736125