子视图UINavigationController泄漏ARC

时间:2015-03-23 17:50:22

标签: ios objective-c memory-leaks uinavigationcontroller

在子视图中呈现和解除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];
}

此时,我的期望是导航控制器的唯一“所有者”是父视图控制器(在本例中为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];
}

当然没有其他东西可以“保留”导航控制器,因为它没有被解除分配。我不认为它可能是保留它的根视图控制器,不是吗?

有些研究表明retainCount没有意义,但是我已经确定在解雇后它仍然是2,我希望它会为零。

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

2 个答案:

答案 0 :(得分:1)

navigationController上的UIViewController属性为retain / strong,这可能是另一个强大的参考。

因此,请尝试从导航控制器弹出所有视图控制器,看看是否有效。

答案 1 :(得分:1)

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

enter image description here

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

触摸“关闭”按钮时,导航控制器及其任何子视图控制器都会被正确释放。