子视图 UINavigationController 泄漏 ARC

Subview UINavigationController Leak ARC

我在子视图中呈现和关闭 UINavigationController 时遇到内存泄漏(UINavigationController 及其根视图控制器都被泄漏)。我介绍导航控制器的方法似乎有点不标准,所以我希望 SO 社区中的人能够提供帮助。

1.介绍

导航控制器呈现如下:

-(void) presentSubNavigationControllerWithRootViewControllerIdentifier:(NSString *)rootViewControllerIdentifier inStoryboardWithName:(NSString *)storyboardName {

    // grab the root view controller from a storyboard
    UIStoryboard * storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
    UIViewController * rootViewController = [storyboard instantiateViewControllerWithIdentifier:rootViewControllerIdentifier];

    // instantiate the navigation controller
    UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController:rootViewController];

    // perform some layout configuration that should be inconsequential to memory management (right?)
    [nc setNavigationBarHidden:YES];
    [nc setEdgesForExtendedLayout:UIRectEdgeLeft | UIRectEdgeRight | UIRectEdgeBottom];
    nc.view.frame = _navControllerParentView.bounds;

    // install the navigation controller (_navControllerParentView is a persisted IBOutlet)
    [_navControllerParentView addSubview:nc.view];

    // strong reference for easy access
    [self setSubNavigationController:nc];
}

在这一点上,我的期望是导航控制器的唯一 "owner" 是父视图控制器(在这种情况下,self)。但是,当如下所示关闭导航控制器时,它不会被释放(因此它的 rootViewController 也被泄漏,依此类推所有权树)。

2。解雇

关闭非常简单,但似乎不足以进行适当的内存管理:

-(void) dismissSubNavigationController {

    // prevent an orphan view from remaining in the view hierarchy
    [_subNavigationController.view removeFromSuperview];

    // release our reference to the navigation controller
    [self setSubNavigationController:nil];
}

肯定还有其他东西是 "retaining" 导航控制器,因为它没有被释放。我不认为它可能是保留它的根视图控制器,是吗?

一些研究表明 retainCount 没有意义,但 FWIW 我确定它在解雇后仍保持在 2,我希望它为零。

是否有完全不同/更好的方法来呈现 subNavigationController?也许在故事板中定义导航控制器比简单地消除对几行代码的需要有更大的好处?

UIViewController 上的 navigationController 属性 是 retain/strong,这大概是另一个强引用。

所以尝试从导航控制器弹出所有视图控制器,看看是否可行。

将一个控制器的视图添加为另一个控制器视图的子视图时,最好将添加的视图的控制器设为子视图控制器;也就是说,您将其添加到其视图的控制器应该实现自定义容器控制器 api。一个简单的设置方法是在情节提要中使用容器视图,它会自动为您提供一个嵌入式控制器(您可以 select 该控制器,然后在编辑菜单中选择嵌入导航控制器以获得 UI 你正在尝试制作)。通常,这个嵌入式视图控制器会在父控制器的视图加载后立即添加,但您可以通过实现 shouldPerformSegueWithIdentifier:sender: 来抑制它。我用这个故事板创建了一个简单的测试应用程序,

ViewController 中用于抑制初始显示的代码,以及随后显示和关闭它的按钮方法如下,

@implementation ViewController

-(BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
    if ([identifier isEqualToString:@"Embed"]) { // The embed segue in IB was given this identifier. This method is not called when calling performSegueWithIdentifier:sender: in code (as in the button method below)
        return NO;
    }else{
        return  YES;
    }
}


- (IBAction)showEmbed:(UIButton *)sender {

    [self performSegueWithIdentifier:@"Embed" sender:self];
}


- (IBAction)dismissEmbed:(UIButton *)sender {
    [[self.childViewControllers.firstObject view] removeFromSuperview];
    [self.childViewControllers.firstObject willMoveToParentViewController:nil];
    [self.childViewControllers.firstObject removeFromParentViewController];
}

@end

当触摸 Dismiss 按钮时,导航控制器及其任何子视图控制器被正确释放。