2015-03-23 2 views
0

Я испытываю утечку памяти (UINavigationController и его корневой контроллер просмотра просачиваются) при представлении и увольнении UINavigationController в подвью. Мой метод представления навигационного контроллера кажется немного нестандартным, поэтому я надеялся, что кто-то из сообщества SO сможет помочь.Subview UINavigationController Leak ARC

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 не имеет смысла, но FWIW Я решил, что он остается в 2 после увольнения, где я ожидаю, что он будет равен нулю.

Есть ли совершенно другой/лучший способ представления subNavigationController? Возможно, определение контроллера навигации в раскадровке будет иметь большую выгоду, чем просто устранение необходимости в нескольких строках кода?

ответ

1

Лучше всего, когда вы добавляете представление контроллера в виде подсмотра другого вида контроллера, чтобы вы добавили контроллер добавленного представления в контроллер детского представления; то есть контроллер, чей вид, с которым вы добавляете, должен реализовать пользовательский контроллер контейнера api. Легкий способ настроить это - использовать представление контейнера в раскадровке, которое дает вам встроенный контроллер автоматически (вы можете выбрать этот контроллер и в меню редактирования выбрать встроенный контроллер навигации, чтобы получить пользовательский интерфейс, который вы пытаетесь делать). Обычно этот встроенный контроллер представлений будет добавлен сразу после загрузки родительского контроллера, но вы можете его подавить, выполнив 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 

навигационный контроллер и любой из его контроллеров детского представления должным образом освобождаются при нажатии кнопки Dismiss.

+0

Спасибо, я ожидаю, что мой «танец вокруг» шаблона VC ребенка, вероятно, стал причиной возникновения этой проблемы. Скоро сделаем снимок в рефакторе. – dave

1

navigationController Недвижимость на UIViewController является retain/strong, что, по-видимому, является другой сильной ссылкой.

Так что попробуйте вытащить все контроллеры просмотра с контроллера навигации и посмотреть, работает ли это.

+0

Я на самом деле пробовал это, и не кажется, что navigationController будет всплывать до нуля! – dave

+0

@dave BTW Я предлагаю вам использовать сдерживание контроллера вида, а не просто добавить его в качестве подсмотра. Не уверен, что это поможет вам или нет. – rounak

+0

Да, возможно, этот подход мог бы решить другой подход. Я надеялся найти решение в контексте самого вопроса, но, по-моему, иногда необходимость переписывать - это реальность. Впечатление, которое я получаю, состоит в том, что навигационные контроллеры вообще не предназначены для освобождения, и я полагаю, что это хорошо, учитывая, насколько они легки. – dave

Смежные вопросы